Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ jobs:
EXCLUDES: '*.jar,exclude_dir'
RCON_PATH: /usr/bin/rcon-cli
DEBUG: true
permissions:
contents: write
services:
registry:
image: registry:2
Expand All @@ -26,11 +28,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v3

# Script needs to be cleaned up a lot for shellcheck'ing
# - name: ShellCheck
# uses: ludeeus/action-shellcheck@1.1.0
# with:
# ignore: tests
# Script needs to be cleaned up a lot for shellcheck'ing
# - name: ShellCheck
# uses: ludeeus/action-shellcheck@1.1.0
# with:
# ignore: tests

- name: Hadolint Action
uses: hadolint/hadolint-action@v3.1.0
Expand Down Expand Up @@ -66,3 +68,9 @@ jobs:
INITIAL_DELAY: 0s
RESTIC_PASSWORD: 1234
run: ./tests/test.simple.restic.sh

- name: Test skip on startup
run: ./tests/skip-on-startup/test.skip-on-startup.sh

- name: Test preserve manual backups
run: ./tests/preserve-manual-backups/test.preserve-manual-backups.sh
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Provides a side-car container to back up [itzg/minecraft-server](https://github.
- `BACKUP_ON_STARTUP`=true : Set to false to skip first backup on startup.
- `PAUSE_IF_NO_PLAYERS`=false
- `PLAYERS_ONLINE_CHECK_INTERVAL`=5m
- `PRESERVE_MANUAL_BACKUPS`=false : Adds a "-preserve" suffix to [on-demand backups](#on-demand-backups) which will be ignored by prune steps
- `PRUNE_BACKUPS_DAYS`=7
- `PRUNE_BACKUPS_COUNT`= -disabled unless set (only works with tar/rsync)
- `PRUNE_RESTIC_RETENTION`=--keep-within 7d
Expand Down
17 changes: 13 additions & 4 deletions scripts/opt/backup-loop.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ fi
: "${BACKUP_ON_STARTUP:=true}"
: "${PAUSE_IF_NO_PLAYERS:=false}"
: "${PLAYERS_ONLINE_CHECK_INTERVAL:=5m}"
: "${PRESERVE_MANUAL_BACKUPS:=false}"
: "${BACKUP_METHOD:=tar}" # currently one of tar, restic, rsync
: "${TAR_COMPRESS_METHOD:=gzip}" # bzip2 gzip zstd
: "${ZSTD_PARAMETERS:=-3 --long=25 --single-thread}"
Expand Down Expand Up @@ -77,6 +78,14 @@ is_one_shot() {
fi
}

PRESERVE_SUFFIX="-preserve"

if is_one_shot && [[ "${PRESERVE_MANUAL_BACKUPS^^}" = TRUE ]]; then
suffix="-preserve"
else
suffix=""
fi

is_paused() {
[[ -e "${SRC_DIR}/.paused" ]]
}
Expand Down Expand Up @@ -229,11 +238,11 @@ tar() {
readarray -td, includes_patterns < <(printf '%s' "${INCLUDES:-.}")

_find_old_backups() {
find "${DEST_DIR}" -maxdepth 1 -name "*.${backup_extension}" -mtime "+${PRUNE_BACKUPS_DAYS}" "${@}"
find "${DEST_DIR}" -maxdepth 1 -name "*.${backup_extension}" -not -name "*${PRESERVE_SUFFIX}*" -mtime "+${PRUNE_BACKUPS_DAYS}" "${@}"
}

_find_extra_backups() {
find "${DEST_DIR}" -maxdepth 1 -name "*.${backup_extension}" -exec ls -NtA {} \+ | \
find "${DEST_DIR}" -maxdepth 1 -name "*.${backup_extension}" -not -name "*${PRESERVE_SUFFIX}*" -exec ls -NtA {} \+ | \
tail -n +$((PRUNE_BACKUPS_COUNT + 1))
}

Expand Down Expand Up @@ -263,7 +272,7 @@ tar() {
}
backup() {
ts=$(date +"%Y%m%d-%H%M%S")
outFile="${DEST_DIR}/${BACKUP_NAME}-${ts}.${backup_extension}"
outFile="${DEST_DIR}/${BACKUP_NAME}-${ts}${suffix}.${backup_extension}"
log INFO "Backing up content in ${SRC_DIR} to ${outFile}"
command tar "${excludes[@]}" "${tar_parameters[@]}" -cf "${outFile}" -C "${SRC_DIR}" "${includes_patterns[@]}" || exitCode=$?
if [ ${exitCode:-0} -eq 0 ]; then
Expand All @@ -275,7 +284,7 @@ tar() {
exit 1
fi
if [ "${LINK_LATEST^^}" == "TRUE" ]; then
ln -sf "${BACKUP_NAME}-${ts}.${backup_extension}" "${DEST_DIR}/latest.${backup_extension}"
ln -sf "${BACKUP_NAME}-${ts}${suffix}.${backup_extension}" "${DEST_DIR}/latest.${backup_extension}"
fi
}
prune() {
Expand Down
4 changes: 4 additions & 0 deletions tests/preserve-manual-backups/docker-compose.override.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
services:
backup:
environment:
PRESERVE_MANUAL_BACKUPS: true
35 changes: 35 additions & 0 deletions tests/preserve-manual-backups/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
services:
mc:
image: itzg/minecraft-server
restart: always
tty: true
stdin_open: true
ports:
- "25565:25565"
environment:
EULA: "TRUE"
ENABLE_AUTOPAUSE: "TRUE"
AUTOPAUSE_TIMEOUT_EST: 30
volumes:
- "./data:/data"

backup:
restart: "no"
build: ../../
depends_on:
mc:
condition: service_healthy
environment:
BACKUP_INTERVAL: "1h"
BACKUP_ON_STARTUP: false # isolate to only ONE_SHOT backups
PRUNE_BACKUPS_DAYS: 1 # create a file 2 days old to get pruned in test
RCON_HOST: mc
INITIAL_DELAY: 0
PAUSE_IF_NO_PLAYERS: true
deploy:
resources:
limits:
memory: 2G
volumes:
- "./data:/data:ro"
- "./backups:/backups"
97 changes: 97 additions & 0 deletions tests/preserve-manual-backups/test.preserve-manual-backups.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env bash

set -x
WORKDIR="$(realpath "$(dirname "${0}")")"
cd "${WORKDIR}/" || exit

mkdir -p ./backups
mkdir -p ./data

cleanup_backups() {
rm -rf ./backups/*
}

cleanup_all() {
docker compose down
cleanup_backups
rm -rf ./data/*
}

## Clean up upon failure or reaching the end
trap cleanup_all EXIT

setup() {
# Set inital exit state to PASS (0)
overall_status=0

# Build from current filesystem
echo "Building..."
docker compose build > /dev/null

echo "Starting server..."
rm -rf ./data/*
docker compose up mc -d > /dev/null
}

old_timestamp=$(TZ=UTC+96 date +%Y%m%d%H%M) # Two days old

run_test1(){
echo -e "\nTest 1: Ensure default behavior, no suffix"
cleanup_backups

docker compose -f docker-compose.yml config

docker compose -f docker-compose.yml run --rm --build backup now
preserved_backup_count=$(find backups/ -name "*.tgz" -name "*preserve*" | wc -l)
not_preserved_backup_count=$(find backups/ -name "*.tgz" -not -name "*preserve*" | wc -l)

tree backups

echo "Preserved backups: ${preserved_backup_count}"
echo "Not-Preserved backups: ${not_preserved_backup_count}"

# Ensure typical backup does not have "-preserve" suffix
if [ 1 -eq "$not_preserved_backup_count" ]; then
echo "PASS"
else
echo "FAIL"
overall_status=1
fi
}

run_test2(){
echo -e "\nTest 2: Ensure added suffix and preserved are not pruned"
cleanup_backups

# Two old backups
touch -t "$old_timestamp" "./backups/fake-backup-preserve.tgz"
touch -t "$old_timestamp" "./backups/fake-backup.tgz"

# Plus current preserved backup
docker compose -f docker-compose.yml -f docker-compose.override.yml run --rm --build backup now > /dev/null

preserved_backup_count=$(find backups/ -name "*.tgz" -name "*preserve*" | wc -l)
not_preserved_backup_count=$(find backups/ -name "*.tgz" -not -name "*preserve*" | wc -l)

tree backups

echo "Preserved backups: ${preserved_backup_count}"
echo "Not-Preserved backups: ${not_preserved_backup_count}"

# Ensure there's
# 2 preserved (1 old + 1 new)
# 0 not preserved (1 pruned)
if [[ 2 -eq "$preserved_backup_count" && 0 -eq "$not_preserved_backup_count" ]]; then
echo "PASS"
else
echo "FAIL"
overall_status=1
fi
}

setup
run_test1
run_test2

exit $overall_status

7 changes: 5 additions & 2 deletions tests/skip-on-startup/test.skip-on-startup.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#!/usr/bin/env bash

set -x
WORKDIR="$(realpath "$(dirname "${0}")")"
cd "${WORKDIR}/" || exit

mkdir -p ./backups
mkdir -p ./data

get_backup_count() {
backup_count=$(ls -1 backups | wc -l)
backup_count=$(find backups/* -prune | wc -l)
echo "Output: ${backup_count} backups"
}

Expand Down Expand Up @@ -84,4 +88,3 @@ run_test2
run_test3

exit $overall_status

5 changes: 3 additions & 2 deletions tests/test.simple.tar.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set -euo pipefail

set -x

WORKDIR="$(readlink -m "$(dirname "${0}")")"
WORKDIR="$(realpath "$(dirname "${0}")")"

cd "${WORKDIR}/.."

Expand Down Expand Up @@ -34,7 +34,8 @@ export RCON_PATH
export PRUNE_BACKUPS_DAYS

mkdir "${EXTRACT_DIR}"
touch -d "$(( PRUNE_BACKUPS_DAYS + 2 )) days ago" "${LOCAL_DEST_DIR}/fake_backup_that_should_be_deleted.tgz"
old_timestamp=$(TZ=UTC+96 date +%Y%m%d%H%M) # Two days old
touch -t "$old_timestamp" "${LOCAL_DEST_DIR}/fake_backup_that_should_be_deleted.tgz"
ls -al "${LOCAL_DEST_DIR}"

timeout 50 docker run --rm \
Expand Down