Overview
I run a private Starbound server, which I have described in great detail on
this site. Of course I need backups!
The script
The backup task, and the corresponding restore task, are all bundled into one
self-contained script! This whole project is just one shell script. Er, and of
course a cron job.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173 |
#!/bin/sh
# File: bup_v2_starbound.sh
# License: CC-BY-SA 4.0
# Author: bgstack15
# Startate: 2020-08-09 13:24
# Title: Incremental Backup and Restore Solution for Starbound
# Purpose: Take backups, full once a week and incremental daily. Full backup happens on Mondays.
# Usage:
# Variables:
# OUTDIR path where backup tarballs are placed
# INFILE input directory to be backed up
# RESTORE=0 Use =1 to choose the restore action instead of backup action.
# BUP_DATE Override builtin TODAY variable (YYYY-MM-DD), primarily for testing purposes
# LOGFILE
# RESTOREPOINT=YYYY-MM-DD Date to restore to, if RESTORE=1. Accepts string "today"
# DESTDIR Path to restore to. It is recommended to restore elsewhere, validate, and then place in real location.
# DRYRUN= Use =1 to not take any actions. Useful with DEBUG=1
# DEBUG= Use =1 to be verbose
# SERVERACTIONSOFF= Use =1 to skip the start/stop service commands
# Examples:
# cronjob: 30 3 * * * steam /usr/bin/sh /home/steam/bin/bup_v2_starbound.sh 1>/dev/null 2>&1
# restore with:
# time SERVERACTIONSOFF=1 DESTDIR=/tmp/universe.restore RESTOREPOINT=today DEBUG=1 RESTORE=1 sh bup_v2_starbound.sh
# Reference:
# https://www.computernetworkingnotes.com/linux-tutorials/create-and-restore-incremental-backups-in-linux-with-tar.html
# Improve:
# Documentation:
# The overall architecture of this tool uses a weekly full backup with daily incrementals strategy. These are mostly hard-coded and will be difficult to change the cadence.
# Define variables
OUTDIR=/mnt/public/Support/Systems/server4/home/steam/Steam/storage
INFILE=/home/steam/Steam/storage/universe
test -z "${RESTORE}" && RESTORE=0 # if RESTORE=1, then we want to perform a restore action.
EXITFILE="$( mktemp )"
trap '_ect=$? ; trap "" 1; rm -f "${EXITFILE:-NOTHINGTODELETE}" ; exit ${_ect} ;' 1
# calculated variables
TODAY="$( date "+%F" )"
test -n "${BUP_DATE}" && TODAY="${BUP_DATE}" # use BUP_DATE to override real date, for testing purposes.
WEEK="$( date -d "${TODAY}" "+%G-W%V" )"
# use incremental backup...
BUP_TYPE="inc"
# unless today is Monday. If you change day of week, then the WEEK value as used for snapshot file might not be useful or correct.
test "$( date -d "${TODAY}" "+%u" )" = "1" && BUP_TYPE="full"
test -z "${LOGFILE}" && LOGFILE="${OUTDIR}/bup.${TODAY}.log"
INFILENAME="$( basename "${INFILE}" )"
# Functions
backup_v2() {
# use variables OUTFILE SNAPSHOTFILE INFILENAME INFILE
# assume already changed directory to the working directory
cd "$( dirname "${INFILE}" )"
tar -czg "${SNAPSHOTFILE}" -f "${OUTFILE}" "${INFILENAME}"
}
restore_v2() {
# needed input: RESTOREPOINT="2020-08-03" DESTDIR="/home/steam/Steam/storage/universe.restored" OUTDIR
# goals: this function calculates which tarballs are needed, that would match that week number up until that date. So that last full one, up until the exact date requested.
matchfile="$( find "${OUTDIR}" -name "${INFILENAME}*" | grep "${RESTOREPOINT}" )"
if test "$( echo "${matchfile}" | wc -l )" != "1" || test "${matchfile}" = "\r" || test "${matchfile}" = "" ;
then
# if the number of matching files for the requested date is not 1, then we have a problem.
echo "Unable to determine which files among ${matchfile} to use. Aborted."
echo "1" > "${EXITFILE}" ; return 1
else
# but now that we have just the one, we need to find its parents back up to the full.
# this awk will look at the 8 preceding filenames to the exact one and then find the last "full" one and all following ones, including the requested one.
#all_files="$( ls -1 "${OUTDIR}" | grep -B8 "${RESTOREPOINT}" | sort -t'-' -k2,3,4 | awk 'BEGIN{a=0} /full/{a=a+1;if(a>1){a=1;for(i in b){b[i]=""};x=0};} a==1{b[x++]=$0;} END{for (i in b){if (b[i]!="")print b[i]}}' )"
all_files="$( find "${OUTDIR}" -maxdepth 1 -name '*z' -printf '%f\n' | sort | grep -B8 "${RESTOREPOINT}" | awk 'BEGIN{a=0} /full/{a=a+1;if(a>1){a=1;for(i in b){b[i]=""};x=0};} a==1{b[x++]=$0;} END{for (i in b){if (b[i]!="")print b[i]}}' | sort )"
# begin the restore
test -n "${_RESTOREDRYRUN}" && lecho "Would restore to date ${RESTOREPOINT} to ${DESTDIR}"
test -n "${_RESTOREDRYRUN}" || { mkdir -p "${DESTDIR}" ; cd "${DESTDIR}" ; }
echo "${all_files}" | while read thisfile ;
do
if test -n "${_RESTOREDRYRUN}" ;
then
lecho "Would extract file ${thisfile}"
else
# do extractions for real
lecho "Extract file ${thisfile}"
tar -zxf "${OUTDIR}/${thisfile}"
fi
done
fi
}
# Main
{
## calculate derived values
OUTFILE="${OUTDIR}/${INFILENAME}.${WEEK}.${TODAY}.${BUP_TYPE}.tar.gz"
SNAPSHOTFILE="${OUTDIR}/snapshot.${WEEK}"
## debug info
test -n "${DEBUG}" && {
case "${RESTORE}" in
0|bup|backup)
echo "OUTFILE ${OUTFILE}"
echo "SNAPSHOTFILE ${SNAPSHOTFILE}"
echo "RESTORE ${RESTORE}"
;;
1|restore)
echo "DESTDIR ${DESTDIR}"
echo "RESTOREPOINT ${RESTOREPOINT}"
;;
esac
echo "LOGFILE ${LOGFILE}"
echo "DRYRUN ${DRYRUN}"
echo "SERVERACTIONSOFF ${SERVERACTIONSOFF}"
}
case "${RESTORE}" in
0|bup|backup) # so take a backup
if test -z "${DRYRUN}" ;
then
test -z "${SERVERACTIONSOFF}" && {
lecho "STOPPING starbound server"
sudo systemctl stop starbound-server.service
}
lecho "START starbound bup to ${OUTFILE}"
backup_v2
lecho "STOP starbound bup"
test -z "${SERVERACTIONSOFF}" && {
lecho "STARTING starbound server"
sudo systemctl start starbound-server.service
}
else
test -z "${SERVERACTIONSOFF}" && lecho "Would STOP starbound server"
lecho "Would run backup"
test -z "${SERVERACTIONSOFF}" && lecho "Would START starbound server"
fi
;;
1|restore) # restore from backup
test -z "${DESTDIR}" && {
echo "Need to provide DESTDIR value. Aborted." 1>&2
echo "1" > "${EXITFILE}" ; exit 1
}
test -z "${RESTOREPOINT}" && {
echo "Need to provide YYYY-MM-DD date for RESTOREPOINT. Aborted." 1>&2
echo "1" > "${EXITFILE}" ; exit 1
}
test "${RESTOREPOINT}" = "today" && RESTOREPOINT="$( date "+%F" )"
if test -z "${DRYRUN}" ;
then
test -z "${SERVERACTIONSOFF}" && {
lecho "STOPPING starbound server"
sudo systemctl stop starbound-server.service
}
lecho "START starbound restore to ${DESTDIR}"
restore_v2
lecho "STOP starbound restore"
test -z "${SERVERACTIONSOFF}" && {
lecho "STARTING starbound server"
sudo systemctl start starbound-server.service
}
else
test -z "${SERVERACTIONSOFF}" && lecho "Would STOP starbound server"
_RESTOREDRYRUN=1 restore_v2
test -z "${SERVERACTIONSOFF}" && lecho "Would START starbound server"
fi
;;
*)
echo "Unknown action ${RESTORE}. No action taken."
;;
esac
} 2>&1 | tee -a "${LOGFILE}"
_ec="$( cat "${EXITFILE}" 2>/dev/null )" ; test -z "${_ec}" && _ec=0
rm -f "${EXITFILE}"
exit ${_ec}
|
It's important to include all the restore logic. What's the point of
incremental backups if you cannot properly restore from them?
Comments