Skip to content

Commit f6fc8d3

Browse files
authored
Merge pull request #641 from github/release-2.22.0
Update backup-utils for changes in 2.22
2 parents 2e71786 + cd619ae commit f6fc8d3

36 files changed

+1755
-136
lines changed

backup.config-example

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ GHE_NUM_SNAPSHOTS=10
5353
# WARNING: do not enable this, only useful for debugging/development
5454
#GHE_BACKUP_FSCK=no
5555

56+
# Cadence of MSSQL backups
57+
# <full>,<differential>,<transactionlog> all in minutes
58+
# e.g.
59+
# - Full backup every week (10080 minutes)
60+
# - Differential backup every day (1440 minutes)
61+
# - Transactionlog backup every 15 minutes
62+
#
63+
GHE_MSSQL_BACKUP_CADENCE=10080,1440,15
64+
5665
# If set to 'yes', ghe-backup jobs will run in parallel. Defaults to 'no'.
5766
#
5867
# WARNING: this feature is in beta.
@@ -77,3 +86,11 @@ GHE_NUM_SNAPSHOTS=10
7786
#
7887
# WARNING: this feature is in beta.
7988
#GHE_PARALLEL_MAX_LOAD=50
89+
90+
# When running an external mysql database, run this script to trigger a MySQL backup
91+
# rather than attempting to backup via backup-utils directly.
92+
#EXTERNAL_DATABASE_BACKUP_SCRIPT="/bin/false"
93+
94+
# When running an external mysql database, run this script to trigger a MySQL restore
95+
# rather than attempting to backup via backup-utils directly.
96+
#EXTERNAL_DATABASE_RESTORE_SCRIPT="/bin/false"

bin/ghe-backup

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,16 @@ ghe-ssh "$GHE_HOSTNAME" -- 'ghe-export-ssh-host-keys' > ssh-host-keys.tar ||
180180
failures="$failures ssh-host-keys"
181181
bm_end "ghe-export-ssh-host-keys"
182182

183-
if is_binary_backup_feature_on; then
184-
echo "Backing up MySQL database using binary backup strategy ..."
185-
else
186-
echo "Backing up MySQL database using logical backup strategy ..."
187-
fi
188183
ghe-backup-mysql || failures="$failures mysql"
189184

185+
if ghe-ssh "$GHE_HOSTNAME" -- 'ghe-config --true app.actions.enabled'; then
186+
echo "Backing up MSSQL databases ..."
187+
ghe-backup-mssql 1>&3 || failures="$failures mssql"
188+
189+
echo "Backing up Actions data ..."
190+
ghe-backup-actions 1>&3 || failures="$failures actions"
191+
fi
192+
190193
commands=("
191194
echo \"Backing up Redis database ...\"
192195
ghe-backup-redis > redis.rdb || printf %s \"redis \" >> \"$failures_file\"")

bin/ghe-restore

Lines changed: 96 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env bash
2-
#/ Usage: ghe-restore [-fchv] [--version] [-s <snapshot-id>] [<host>]
2+
#/ Usage: ghe-restore [-cfhv] [--version] [--skip-mysql] [-s <snapshot-id>] [<host>]
33
#/
44
#/ Restores a GitHub instance from local backup snapshots.
55
#/
@@ -9,40 +9,54 @@
99
#/ <https://enterprise.github.com/help/articles/adding-an-ssh-key-for-shell-access>
1010
#/
1111
#/ OPTIONS:
12-
#/ -f | --force Don't prompt for confirmation before restoring.
13-
#/ -c | --config Restore appliance settings and license in addition to
14-
#/ datastores. Settings are not restored by default to
15-
#/ prevent overwriting different configuration on the
16-
#/ restore host.
17-
#/ -v | --verbose Enable verbose output.
18-
#/ -h | --help Show this message.
19-
#/ --version Display version information and exit.
20-
#/ -s <snapshot-id> Restore from the snapshot with the given id. Available
21-
#/ snapshots may be listed under the data directory.
22-
#/ <host> The <host> is the hostname or IP of the GitHub Enterprise
23-
#/ instance. The <host> may be omitted when the
24-
#/ GHE_RESTORE_HOST config variable is set in backup.config.
25-
#/ When a <host> argument is provided, it always overrides
26-
#/ the configured restore host.
12+
#/ -c | --config Restore appliance settings and license in addition to
13+
#/ datastores. Settings are not restored by default to
14+
#/ prevent overwriting different configuration on the
15+
#/ restore host.
16+
#/ -f | --force Don't prompt for confirmation before restoring.
17+
#/ -h | --help Show this message.
18+
#/ -v | --verbose Enable verbose output.
19+
#/ --skip-mysql Skip MySQL restore steps. Only applicable to external databases.
20+
#/ --version Display version information and exit.
21+
#/
22+
#/ -s <snapshot-id> Restore from the snapshot with the given id. Available
23+
#/ snapshots may be listed under the data directory.
24+
#/
25+
#/ <host> The <host> is the hostname or IP of the GitHub Enterprise
26+
#/ instance. The <host> may be omitted when the
27+
#/ GHE_RESTORE_HOST config variable is set in backup.config.
28+
#/ When a <host> argument is provided, it always overrides
29+
#/ the configured restore host.
2730
#/
2831

2932
set -e
3033

3134
# Parse arguments
32-
restore_settings=false
33-
force=false
35+
: ${RESTORE_SETTINGS:=false}
36+
export RESTORE_SETTINGS
37+
38+
: ${FORCE:=false}
39+
export FORCE
40+
41+
: ${SKIP_MYSQL:=false}
42+
export SKIP_MYSQL
43+
3444
while true; do
3545
case "$1" in
46+
--skip-mysql)
47+
SKIP_MYSQL=true
48+
shift
49+
;;
3650
-f|--force)
37-
force=true
51+
FORCE=true
3852
shift
3953
;;
4054
-s)
4155
snapshot_id="$(basename "$2")"
4256
shift 2
4357
;;
4458
-c|--config)
45-
restore_settings=true
59+
RESTORE_SETTINGS=true
4660
shift
4761
;;
4862
-h|--help)
@@ -116,10 +130,10 @@ ghe_remote_version_required "$GHE_HOSTNAME"
116130

117131
# Figure out if this instance has been configured or is entirely new.
118132
instance_configured=false
119-
if ghe-ssh "$GHE_HOSTNAME" -- "[ -f '$GHE_REMOTE_ROOT_DIR/etc/github/configured' ]"; then
133+
if is_instance_configured; then
120134
instance_configured=true
121135
else
122-
restore_settings=true
136+
RESTORE_SETTINGS=true
123137
fi
124138

125139
# Figure out if we're restoring into cluster
@@ -137,6 +151,11 @@ if ! $CLUSTER && [ "$GHE_BACKUP_STRATEGY" = "cluster" ]; then
137151
exit 1
138152
fi
139153

154+
# Ensure target appliance and restore snapshot are a compatible combination with respect to BYODB
155+
if ! ghe-restore-external-database-compatibility-check; then
156+
exit 1
157+
fi
158+
140159
# Figure out if this appliance is in a replication pair
141160
if ghe-ssh "$GHE_HOSTNAME" -- \
142161
"[ -f '$GHE_REMOTE_ROOT_DIR/etc/github/repl-state' ]"; then
@@ -149,28 +168,30 @@ fi
149168
# important data on the destination appliance that cannot be recovered. This is
150169
# mostly to prevent accidents where the backup host is given to restore instead
151170
# of a separate restore host since they're used in such close proximity.
152-
if $instance_configured && ! $force; then
171+
if $instance_configured && ! $FORCE; then
153172
echo
154173
echo "WARNING: All data on GitHub Enterprise appliance $hostname ($GHE_REMOTE_VERSION)"
155174
echo " will be overwritten with data from snapshot ${GHE_RESTORE_SNAPSHOT}."
156-
echo "Please verify that this is the correct restore host before continuing."
157-
printf "Type 'yes' to continue: "
175+
echo
158176

159-
while read -r response; do
160-
case $response in
161-
yes|Yes|YES)
162-
break
163-
;;
164-
'')
165-
printf "Type 'yes' to continue: "
166-
;;
167-
*)
168-
echo "Restore aborted." 1>&2
169-
exit 1
170-
;;
171-
esac
172-
done
177+
if is_external_database_snapshot && $RESTORE_SETTINGS; then
178+
echo "WARNING: This operation will also restore the external MySQL connection configuration,"
179+
echo " which may be dangerous if the GHES appliance the snapshot was taken from is still online."
180+
echo
181+
fi
182+
183+
prompt_for_confirmation "Please verify that this is the correct restore host before continuing."
184+
fi
185+
186+
# Prompt to verify that restoring BYODB snapshot to unconfigured instance
187+
# will result in BYODB connection information being restored as well.
188+
if is_external_database_snapshot && ! $instance_configured && ! $FORCE; then
173189
echo
190+
echo "WARNING: This operation will also restore the external MySQL connection configuration,"
191+
echo " which may be dangerous if the GHES appliance the snapshot was taken from is still online."
192+
echo
193+
194+
prompt_for_confirmation "Please confirm this before continuing."
174195
fi
175196

176197
# Log restore start message locally and in /var/log/syslog on remote instance
@@ -207,6 +228,14 @@ if $instance_configured; then
207228
fi
208229
fi
209230

231+
# Make sure the GitHub appliance has Actions enabled if the snapshot contains Actions data.
232+
if [ -d "$GHE_RESTORE_SNAPSHOT_PATH/mssql" ] || [ -d "$GHE_RESTORE_SNAPSHOT_PATH/actions" ]; then
233+
if ! ghe-ssh "$GHE_HOSTNAME" -- 'ghe-config --true app.actions.enabled'; then
234+
echo "Error: $GHE_HOSTNAME must have GitHub Actions enabled before restoring since the snapshot contains Actions data. Aborting." 1>&2
235+
exit 1
236+
fi
237+
fi
238+
210239
# Create benchmark file
211240
bm_init > /dev/null
212241

@@ -236,7 +265,7 @@ fi
236265

237266
# Restore settings and license if restoring to an unconfigured appliance or when
238267
# specified manually.
239-
if $restore_settings; then
268+
if $RESTORE_SETTINGS; then
240269
ghe-restore-settings "$GHE_HOSTNAME"
241270
fi
242271

@@ -257,20 +286,39 @@ if [ -s "$GHE_RESTORE_SNAPSHOT_PATH/uuid" ] && ! $CLUSTER; then
257286
ghe-ssh "$GHE_HOSTNAME" -- "sudo rm -rf /data/user/consul/raft"
258287
fi
259288

260-
if is_binary_backup_feature_on; then
261-
appliance_strategy="binary"
289+
if is_external_database_snapshot; then
290+
appliance_strategy="external"
291+
backup_snapshot_strategy="external"
262292
else
263-
appliance_strategy="logical"
293+
if is_binary_backup_feature_on; then
294+
appliance_strategy="binary"
295+
else
296+
appliance_strategy="logical"
297+
fi
298+
299+
if is_binary_backup "$GHE_DATA_DIR/$GHE_RESTORE_SNAPSHOT"; then
300+
backup_snapshot_strategy="binary"
301+
else
302+
backup_snapshot_strategy="logical"
303+
fi
264304
fi
265305

266-
if is_binary_backup "$GHE_DATA_DIR/$GHE_RESTORE_SNAPSHOT"; then
267-
backup_snapshot_strategy="binary"
306+
if is_external_database_target_or_snapshot && $SKIP_MYSQL; then
307+
echo "Skipping MySQL restore."
268308
else
269-
backup_snapshot_strategy="logical"
309+
echo "Restoring MySQL database from ${backup_snapshot_strategy} backup snapshot on an appliance configured for ${appliance_strategy} backups ..."
310+
ghe-restore-mysql "$GHE_HOSTNAME" 1>&3
270311
fi
271312

272-
echo "Restoring MySQL database from ${backup_snapshot_strategy} backup snapshot on an appliance configured for ${appliance_strategy} backups ..."
273-
ghe-restore-mysql "$GHE_HOSTNAME" 1>&3
313+
if ghe-ssh "$GHE_HOSTNAME" -- 'ghe-config --true app.actions.enabled'; then
314+
echo "Restoring MSSQL databases ..."
315+
ghe-restore-mssql "$GHE_HOSTNAME" 1>&3
316+
317+
echo "Restoring Actions data ..."
318+
ghe-restore-actions "$GHE_HOSTNAME" 1>&3
319+
echo "* WARNING: Every self-hosted Actions runner that communicates with the restored GHES server must be restarted or reconfigured in order to continue functioning."
320+
echo " See https://docs.github.com/en/actions/hosting-your-own-runners/adding-self-hosted-runners for more details on how to reconfigure self-hosted Actions runners."
321+
fi
274322

275323
commands=("
276324
echo \"Restoring Redis database ...\"

docs/backup-snapshot-file-structure.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ most recent successful snapshot:
3232
|- enterprise.ghl
3333
|- es-scan-complete
3434
|- manage-password
35+
|- mssql
3536
|- mysql.sql.gz
3637
|- redis.rdb
3738
|- settings.json
@@ -44,3 +45,20 @@ most recent successful snapshot:
4445

4546
Note: the `GHE_DATA_DIR` variable set in `backup.config` can be used to change
4647
the disk location where snapshots are written.
48+
49+
## MS SQL Server backup structure
50+
Actions service uses MS SQL Server as backend data store. Each snapshot includes a suite of backup files for MS SQL Server database(s).
51+
52+
To save time in backup, a three-level backup strategy is implemented. Based on the `GHE_MSSQL_BACKUP_CADENCE` setting, at each snapshot, either a (**F**)ull backup, a (**D**)ifferential or a (**T**)ransaction log backup is taken.
53+
54+
As a result, a suite always contains following for each database: a full backup, possibly a differential backup and at least one transaction log backup. Their relationship with timeline is demonstrated below:
55+
```
56+
M---8:00--16:00---T---8:00--16:00---W... (timeline)
57+
58+
F-----------------F-----------------F... (full backup)
59+
#-----D-----D-----#-----D-----D-----#... (differential backup)
60+
T--T--T--T--T--T--T--T--T--T--T--T--T... (transaction log backup)
61+
```
62+
To save disk space, at each snapshot, hard links are created to point to previous backup files. Only newly-created backup files are transferred from appliance to backup host. When a new full/differential backup is created, they become the new source for hard links and new base line for transaction log backups, for subsequent snapshots.
63+
64+
During restore, a suite of backup files are restored in the sequence of full -> differential -> chronological transaction log.

docs/usage.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ You can supply your own configuration file or use the example configuration file
1616

1717
An example configuration file with documentation on possible settings can found in [backup.config-example](../backup.config-example).
1818

19+
There are a number of command line options that can also be passed to the `ghe-restore` command. Of particular note, if you use an external MySQL service but are restoring from a snapshot prior to enabling this, or vice versa, you must migrate the MySQL data outside of the context of backup-utils first, then pass the `--skip-mysql` flag to `ghe-restore`.
20+
1921
## Example backup and restore usage
2022

2123
The following assumes that `GHE_HOSTNAME` is set to "github.example.com" in
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/usr/bin/env bash
2+
#/ Usage: ghe-backup-actions
3+
#/ Take an online, incremental snapshot of all Actions data (excluding
4+
#/ what is stored in MSSQL)
5+
#/
6+
#/ Note: This command typically isn't called directly. It's invoked by
7+
#/ ghe-backup.
8+
set -e
9+
10+
# Bring in the backup configuration
11+
# shellcheck source=share/github-backup-utils/ghe-backup-config
12+
. "$( dirname "${BASH_SOURCE[0]}" )/ghe-backup-config"
13+
14+
bm_start "$(basename $0)"
15+
16+
# Set up remote host and root backup snapshot directory based on config
17+
port=$(ssh_port_part "$GHE_HOSTNAME")
18+
host=$(ssh_host_part "$GHE_HOSTNAME")
19+
backup_dir="$GHE_SNAPSHOT_DIR/actions"
20+
21+
# Verify rsync is available.
22+
if ! rsync --version 1>/dev/null 2>&1; then
23+
echo "Error: rsync not found." 1>&2
24+
exit 1
25+
fi
26+
27+
# Perform a host-check and establish GHE_REMOTE_XXX variables.
28+
ghe_remote_version_required "$host"
29+
30+
# Make sure root backup dir exists if this is the first run
31+
mkdir -p "$backup_dir"
32+
33+
# If we have a previous increment and it is not empty, avoid transferring existing files via rsync's
34+
# --link-dest support. This also decreases physical space usage considerably.
35+
if [ -d "$GHE_DATA_DIR/current/actions" ] && [ "$(ls -A $GHE_DATA_DIR/current/actions)" ]; then
36+
link_dest="--link-dest=$GHE_DATA_DIR/current/actions"
37+
fi
38+
39+
# Transfer all Actions data from the user data directory using rsync.
40+
ghe_verbose "* Transferring Actions files from $host ..."
41+
42+
ghe-rsync -avz \
43+
-e "ghe-ssh -p $port" \
44+
--rsync-path='sudo -u actions rsync' \
45+
--exclude "mutexes" --exclude "dumps" \
46+
$link_dest \
47+
"$host:$GHE_REMOTE_DATA_USER_DIR/actions/" \
48+
"$GHE_SNAPSHOT_DIR/actions" 1>&3
49+
50+
bm_end "$(basename $0)"

0 commit comments

Comments
 (0)