Pass shellcheck
This commit is contained in:
parent
71311e2321
commit
80abd684ba
3 changed files with 61 additions and 43 deletions
85
backup.sh
85
backup.sh
|
@ -26,6 +26,13 @@ WINDOW_MANAGER="screen" # Choices: screen, tmux
|
||||||
DATE_FORMAT="%F_%H-%M-%S"
|
DATE_FORMAT="%F_%H-%M-%S"
|
||||||
TIMESTAMP=$(date +$DATE_FORMAT)
|
TIMESTAMP=$(date +$DATE_FORMAT)
|
||||||
|
|
||||||
|
log-fatal () {
|
||||||
|
echo -e "\033[0;31mFATAL:\033[0m $*"
|
||||||
|
}
|
||||||
|
log-warning () {
|
||||||
|
echo -e "\033[0;33mWARNING:\033[0m $*"
|
||||||
|
}
|
||||||
|
|
||||||
while getopts 'a:cd:e:f:hi:l:m:o:p:qs:vw:' FLAG; do
|
while getopts 'a:cd:e:f:hi:l:m:o:p:qs:vw:' FLAG; do
|
||||||
case $FLAG in
|
case $FLAG in
|
||||||
a) COMPRESSION_ALGORITHM=$OPTARG ;;
|
a) COMPRESSION_ALGORITHM=$OPTARG ;;
|
||||||
|
@ -61,20 +68,14 @@ while getopts 'a:cd:e:f:hi:l:m:o:p:qs:vw:' FLAG; do
|
||||||
s) SCREEN_NAME=$OPTARG ;;
|
s) SCREEN_NAME=$OPTARG ;;
|
||||||
v) DEBUG=true ;;
|
v) DEBUG=true ;;
|
||||||
w) WINDOW_MANAGER=$OPTARG ;;
|
w) WINDOW_MANAGER=$OPTARG ;;
|
||||||
|
*) log-fatal "Invalid option -$FLAG"; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
log-fatal () {
|
|
||||||
echo -e "\033[0;31mFATAL:\033[0m $*"
|
|
||||||
}
|
|
||||||
log-warning () {
|
|
||||||
echo -e "\033[0;33mWARNING:\033[0m $*"
|
|
||||||
}
|
|
||||||
|
|
||||||
rcon-command () {
|
rcon-command () {
|
||||||
HOST="$(echo $1 | cut -d: -f1)"
|
HOST="$(echo "$1" | cut -d: -f1)"
|
||||||
PORT="$(echo $1 | cut -d: -f2)"
|
PORT="$(echo "$1" | cut -d: -f2)"
|
||||||
PASSWORD="$(echo $1 | cut -d: -f3-)"
|
PASSWORD="$(echo "$1" | cut -d: -f3-)"
|
||||||
COMMAND="$2"
|
COMMAND="$2"
|
||||||
|
|
||||||
reverse-hex-endian () {
|
reverse-hex-endian () {
|
||||||
|
@ -255,50 +256,54 @@ message-players "Starting backup..." "$ARCHIVE_FILE_NAME"
|
||||||
|
|
||||||
# Parse file timestamp to one readable by "date"
|
# Parse file timestamp to one readable by "date"
|
||||||
parse-file-timestamp () {
|
parse-file-timestamp () {
|
||||||
local DATE_STRING=$(echo $1 | awk -F_ '{gsub(/-/,":",$2); print $1" "$2}')
|
local DATE_STRING
|
||||||
echo $DATE_STRING
|
DATE_STRING="$(echo "$1" | awk -F_ '{gsub(/-/,":",$2); print $1" "$2}')"
|
||||||
|
echo "$DATE_STRING"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Delete a backup
|
# Delete a backup
|
||||||
delete-backup () {
|
delete-backup () {
|
||||||
local BACKUP=$1
|
local BACKUP=$1
|
||||||
rm $BACKUP_DIRECTORY/$BACKUP
|
rm "$BACKUP_DIRECTORY"/"$BACKUP"
|
||||||
message-players "Deleted old backup" "$BACKUP"
|
message-players "Deleted old backup" "$BACKUP"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Sequential delete method
|
# Sequential delete method
|
||||||
delete-sequentially () {
|
delete-sequentially () {
|
||||||
local BACKUPS=($(ls $BACKUP_DIRECTORY))
|
local BACKUPS=("$BACKUP_DIRECTORY"/*)
|
||||||
while [[ $MAX_BACKUPS -ge 0 && ${#BACKUPS[@]} -gt $MAX_BACKUPS ]]; do
|
while [[ $MAX_BACKUPS -ge 0 && ${#BACKUPS[@]} -gt $MAX_BACKUPS ]]; do
|
||||||
delete-backup ${BACKUPS[0]}
|
delete-backup "$(basename "${BACKUPS[0]}")"
|
||||||
BACKUPS=($(ls $BACKUP_DIRECTORY))
|
BACKUPS=("$BACKUP_DIRECTORY"/*)
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
# Functions to sort backups into correct categories based on timestamps
|
# Functions to sort backups into correct categories based on timestamps
|
||||||
is-hourly-backup () {
|
is-hourly-backup () {
|
||||||
local TIMESTAMP=$*
|
local TIMESTAMP=$*
|
||||||
local MINUTE=$(date -d "$TIMESTAMP" +%M)
|
local MINUTE
|
||||||
return $MINUTE
|
MINUTE=$(date -d "$TIMESTAMP" +%M)
|
||||||
|
return "$MINUTE"
|
||||||
}
|
}
|
||||||
is-daily-backup () {
|
is-daily-backup () {
|
||||||
local TIMESTAMP=$*
|
local TIMESTAMP=$*
|
||||||
local HOUR=$(date -d "$TIMESTAMP" +%H)
|
local HOUR
|
||||||
return $HOUR
|
HOUR=$(date -d "$TIMESTAMP" +%H)
|
||||||
|
return "$HOUR"
|
||||||
}
|
}
|
||||||
is-weekly-backup () {
|
is-weekly-backup () {
|
||||||
local TIMESTAMP=$*
|
local TIMESTAMP=$*
|
||||||
local DAY=$(date -d "$TIMESTAMP" +%u)
|
local DAY
|
||||||
return $((DAY - 1))
|
DAY=$(date -d "$TIMESTAMP" +%u)
|
||||||
|
return "$((DAY - 1))"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Helper function to sum an array
|
# Helper function to sum an array
|
||||||
array-sum () {
|
array-sum () {
|
||||||
SUM=0
|
SUM=0
|
||||||
for NUMBER in $*; do
|
for NUMBER in "$@"; do
|
||||||
(( SUM += NUMBER ))
|
(( SUM += NUMBER ))
|
||||||
done
|
done
|
||||||
echo $SUM
|
echo "$SUM"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Thinning delete method
|
# Thinning delete method
|
||||||
|
@ -310,7 +315,7 @@ delete-thinning () {
|
||||||
local BLOCK_FUNCTIONS=("is-hourly-backup" "is-daily-backup" "is-weekly-backup")
|
local BLOCK_FUNCTIONS=("is-hourly-backup" "is-daily-backup" "is-weekly-backup")
|
||||||
|
|
||||||
# Warn if $MAX_BACKUPS does not have enough room for all the blocks
|
# Warn if $MAX_BACKUPS does not have enough room for all the blocks
|
||||||
TOTAL_BLOCK_SIZE=$(array-sum ${BLOCK_SIZES[@]})
|
TOTAL_BLOCK_SIZE=$(array-sum "${BLOCK_SIZES[@]}")
|
||||||
if [[ $MAX_BACKUPS != -1 ]] && [[ $TOTAL_BLOCK_SIZE -gt $MAX_BACKUPS ]]; then
|
if [[ $MAX_BACKUPS != -1 ]] && [[ $TOTAL_BLOCK_SIZE -gt $MAX_BACKUPS ]]; then
|
||||||
if ! $SUPPRESS_WARNINGS; then
|
if ! $SUPPRESS_WARNINGS; then
|
||||||
log-warning "MAX_BACKUPS ($MAX_BACKUPS) is smaller than TOTAL_BLOCK_SIZE ($TOTAL_BLOCK_SIZE)"
|
log-warning "MAX_BACKUPS ($MAX_BACKUPS) is smaller than TOTAL_BLOCK_SIZE ($TOTAL_BLOCK_SIZE)"
|
||||||
|
@ -318,19 +323,21 @@ delete-thinning () {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local CURRENT_INDEX=0
|
local CURRENT_INDEX=0
|
||||||
local BACKUPS=($(ls -r $BACKUP_DIRECTORY)) # List newest first
|
local BACKUPS=("$BACKUP_DIRECTORY"/*) # List newest first
|
||||||
|
|
||||||
for BLOCK_INDEX in ${!BLOCK_SIZES[@]}; do
|
for BLOCK_INDEX in "${!BLOCK_SIZES[@]}"; do
|
||||||
local BLOCK_SIZE=${BLOCK_SIZES[BLOCK_INDEX]}
|
local BLOCK_SIZE=${BLOCK_SIZES[BLOCK_INDEX]}
|
||||||
local BLOCK_FUNCTION=${BLOCK_FUNCTIONS[BLOCK_INDEX]}
|
local BLOCK_FUNCTION=${BLOCK_FUNCTIONS[BLOCK_INDEX]}
|
||||||
local OLDEST_BACKUP_IN_BLOCK_INDEX=$((BLOCK_SIZE + CURRENT_INDEX)) # Not an off-by-one error because a new backup was already saved
|
local OLDEST_BACKUP_IN_BLOCK_INDEX=$((BLOCK_SIZE + CURRENT_INDEX)) # Not an off-by-one error because a new backup was already saved
|
||||||
local OLDEST_BACKUP_IN_BLOCK=${BACKUPS[OLDEST_BACKUP_IN_BLOCK_INDEX]}
|
local OLDEST_BACKUP_IN_BLOCK
|
||||||
|
OLDEST_BACKUP_IN_BLOCK="$(basename "${BACKUPS[OLDEST_BACKUP_IN_BLOCK_INDEX]}")"
|
||||||
|
|
||||||
if [[ $OLDEST_BACKUP_IN_BLOCK == "" ]]; then
|
if [[ "$OLDEST_BACKUP_IN_BLOCK" == "" ]]; then
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local OLDEST_BACKUP_TIMESTAMP=$(parse-file-timestamp ${OLDEST_BACKUP_IN_BLOCK:0:19})
|
local OLDEST_BACKUP_TIMESTAMP
|
||||||
|
OLDEST_BACKUP_TIMESTAMP=$(parse-file-timestamp "${OLDEST_BACKUP_IN_BLOCK:0:19}")
|
||||||
local BLOCK_COMMAND="$BLOCK_FUNCTION $OLDEST_BACKUP_TIMESTAMP"
|
local BLOCK_COMMAND="$BLOCK_FUNCTION $OLDEST_BACKUP_TIMESTAMP"
|
||||||
|
|
||||||
if $BLOCK_COMMAND; then
|
if $BLOCK_COMMAND; then
|
||||||
|
@ -340,7 +347,7 @@ delete-thinning () {
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# Oldest backup in this block does not satisfy the condition for placement in next block
|
# Oldest backup in this block does not satisfy the condition for placement in next block
|
||||||
delete-backup $OLDEST_BACKUP_IN_BLOCK
|
delete-backup "$OLDEST_BACKUP_IN_BLOCK"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -351,7 +358,7 @@ delete-thinning () {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Ensure directory exists
|
# Ensure directory exists
|
||||||
mkdir -p "$(dirname $ARCHIVE_PATH)"
|
mkdir -p "$(dirname "$ARCHIVE_PATH")"
|
||||||
|
|
||||||
# Disable world autosaving
|
# Disable world autosaving
|
||||||
execute-command "save-off"
|
execute-command "save-off"
|
||||||
|
@ -360,10 +367,10 @@ execute-command "save-off"
|
||||||
START_TIME=$(date +"%s")
|
START_TIME=$(date +"%s")
|
||||||
case $COMPRESSION_ALGORITHM in
|
case $COMPRESSION_ALGORITHM in
|
||||||
# No compression
|
# No compression
|
||||||
"") tar -cf $ARCHIVE_PATH -C $SERVER_WORLD .
|
"") tar -cf "$ARCHIVE_PATH" -C "$SERVER_WORLD" .
|
||||||
;;
|
;;
|
||||||
# With compression
|
# With compression
|
||||||
*) tar -cf - -C $SERVER_WORLD . | $COMPRESSION_ALGORITHM -cv -$COMPRESSION_LEVEL - > $ARCHIVE_PATH 2>> /dev/null
|
*) tar -cf - -C "$SERVER_WORLD" . | $COMPRESSION_ALGORITHM -cv -"$COMPRESSION_LEVEL" - > "$ARCHIVE_PATH" 2>> /dev/null
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
sync
|
sync
|
||||||
|
@ -386,15 +393,15 @@ delete-old-backups () {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Notify players of completion
|
# Notify players of completion
|
||||||
WORLD_SIZE_BYTES=$(du -b --max-depth=0 $SERVER_WORLD | awk '{print $1}')
|
WORLD_SIZE_BYTES=$(du -b --max-depth=0 "$SERVER_WORLD" | awk '{print $1}')
|
||||||
ARCHIVE_SIZE_BYTES=$(du -b $ARCHIVE_PATH | awk '{print $1}')
|
ARCHIVE_SIZE_BYTES=$(du -b "$ARCHIVE_PATH" | awk '{print $1}')
|
||||||
ARCHIVE_SIZE=$(du -h $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}')
|
BACKUP_DIRECTORY_SIZE=$(du -h --max-depth=0 "$BACKUP_DIRECTORY" | awk '{print $1}')
|
||||||
TIME_DELTA=$((END_TIME - START_TIME))
|
TIME_DELTA=$((END_TIME - START_TIME))
|
||||||
|
|
||||||
# Check that archive size is not null and at least 200 Bytes
|
# Check that archive size is not null and at least 200 Bytes
|
||||||
if [[ "$WORLD_SIZE_BYTES" -gt 0 && "$ARCHIVE_SIZE" != "" && "$ARCHIVE_SIZE_BYTES" -gt 200 ]]; then
|
if [[ "$WORLD_SIZE_BYTES" -gt 0 && "$ARCHIVE_SIZE" != "" && "$ARCHIVE_SIZE_BYTES" -gt 200 ]]; then
|
||||||
COMPRESSION_PERCENT=$(($ARCHIVE_SIZE_BYTES * 100 / $WORLD_SIZE_BYTES))
|
COMPRESSION_PERCENT=$((ARCHIVE_SIZE_BYTES * 100 / WORLD_SIZE_BYTES))
|
||||||
message-players-success "Backup complete!" "$TIME_DELTA s, $ARCHIVE_SIZE/$BACKUP_DIRECTORY_SIZE, $COMPRESSION_PERCENT%"
|
message-players-success "Backup complete!" "$TIME_DELTA s, $ARCHIVE_SIZE/$BACKUP_DIRECTORY_SIZE, $COMPRESSION_PERCENT%"
|
||||||
delete-old-backups
|
delete-old-backups
|
||||||
else
|
else
|
||||||
|
|
6
rcon.sh
6
rcon.sh
|
@ -1,9 +1,9 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
rcon-command () {
|
rcon-command () {
|
||||||
HOST="$(echo $1 | cut -d: -f1)"
|
HOST="$(echo "$1" | cut -d: -f1)"
|
||||||
PORT="$(echo $1 | cut -d: -f2)"
|
PORT="$(echo "$1" | cut -d: -f2)"
|
||||||
PASSWORD="$(echo $1 | cut -d: -f3-)"
|
PASSWORD="$(echo "$1" | cut -d: -f3-)"
|
||||||
COMMAND="$2"
|
COMMAND="$2"
|
||||||
|
|
||||||
reverse-hex-endian () {
|
reverse-hex-endian () {
|
||||||
|
|
13
test/test.sh
13
test/test.sh
|
@ -21,7 +21,10 @@ setUp () {
|
||||||
tmux send-keys -t "$SCREEN_TMP" "cat > $TEST_TMP/tmux-output" ENTER
|
tmux send-keys -t "$SCREEN_TMP" "cat > $TEST_TMP/tmux-output" ENTER
|
||||||
python test/mock_rcon.py "$RCON_PORT" "$RCON_PASSWORD" > "$TEST_TMP/rcon-output" &
|
python test/mock_rcon.py "$RCON_PORT" "$RCON_PASSWORD" > "$TEST_TMP/rcon-output" &
|
||||||
echo "$!" > "$TEST_TMP/rcon-pid"
|
echo "$!" > "$TEST_TMP/rcon-pid"
|
||||||
sleep 1
|
|
||||||
|
while ! [[ (-f "$TEST_TMP/screen-output") && (-f "$TEST_TMP/tmux-output") && (-f "$TEST_TMP/rcon-output") ]]; do
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
tearDown () {
|
tearDown () {
|
||||||
|
@ -106,6 +109,13 @@ test-missing-options-suppress-warnings () {
|
||||||
assertNotContains "$OUTPUT" "Minecraft screen/tmux/rcon location not specified (use -s)"
|
assertNotContains "$OUTPUT" "Minecraft screen/tmux/rcon location not specified (use -s)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test-invalid-options () {
|
||||||
|
OUTPUT="$(./backup.sh -z 2>&1)"
|
||||||
|
EXIT_CODE="$?"
|
||||||
|
assertEquals 1 "$EXIT_CODE"
|
||||||
|
assertContains "$OUTPUT" "Invalid option"
|
||||||
|
}
|
||||||
|
|
||||||
test-empty-world-warning () {
|
test-empty-world-warning () {
|
||||||
mkdir -p "$TEST_TMP/server/empty-world"
|
mkdir -p "$TEST_TMP/server/empty-world"
|
||||||
OUTPUT="$(./backup.sh -v -i "$TEST_TMP/server/empty-world" -o "$TEST_TMP/backups" -s "$SCREEN_TMP" -f "$TIMESTAMP" 2>&1)"
|
OUTPUT="$(./backup.sh -v -i "$TEST_TMP/server/empty-world" -o "$TEST_TMP/backups" -s "$SCREEN_TMP" -f "$TIMESTAMP" 2>&1)"
|
||||||
|
@ -158,6 +168,7 @@ test-sequential-delete () {
|
||||||
TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01 +$i hour")"
|
TIMESTAMP="$(date +%F_%H-%M-%S --date="2021-01-01 +$i hour")"
|
||||||
check-backup "$TIMESTAMP.tar.gz"
|
check-backup "$TIMESTAMP.tar.gz"
|
||||||
done
|
done
|
||||||
|
assertEquals 10 "$(find "$TEST_TMP/backups" -type f | wc -l)"
|
||||||
}
|
}
|
||||||
|
|
||||||
test-thinning-delete () {
|
test-thinning-delete () {
|
||||||
|
|
Loading…
Add table
Reference in a new issue