Skip to content

Commit 1eebd8e

Browse files
committed
Merge pull request #156 from github/cluster
GitHub Enterprise Cluster support + SAML fixes
2 parents 87f87c2 + 71efc05 commit 1eebd8e

21 files changed

+1295
-41
lines changed

bin/ghe-backup

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,13 @@ echo "Starting backup of $GHE_HOSTNAME in snapshot $GHE_SNAPSHOT_TIMESTAMP"
8686
ghe_remote_version_required
8787
echo "$GHE_REMOTE_VERSION" > version
8888

89+
# Figure out if we're restoring into cluster
90+
cluster=false
91+
if ghe-ssh "$GHE_HOSTNAME" -- \
92+
"[ -f '$GHE_REMOTE_ROOT_DIR/etc/github/cluster' ]"; then
93+
cluster=true
94+
fi
95+
8996
# Log backup start message in /var/log/syslog on remote instance
9097
ghe_remote_logger "Starting backup from $(hostname) in snapshot $GHE_SNAPSHOT_TIMESTAMP ..."
9198

@@ -99,8 +106,13 @@ if [ $GHE_VERSION_MAJOR -eq 1 -a $GHE_VERSION_PATCH -lt 340 ]; then
99106
GHE_BACKUP_STRATEGY="tarball"
100107
fi
101108

109+
if $cluster; then
110+
GHE_BACKUP_STRATEGY="cluster"
111+
fi
112+
102113
# Record the strategy with the snapshot so we will know how to restore.
103114
echo "$GHE_BACKUP_STRATEGY" > strategy
115+
export GHE_BACKUP_STRATEGY
104116

105117
# If we're using the tarball backup strategy, put the appliance in maintenance
106118
# mode and wait for all writing processes to bleed out.
@@ -127,8 +139,21 @@ ghe-ssh "$GHE_HOSTNAME" -- /bin/bash > mysql.sql.gz ||
127139
failures="$failures mysql"
128140

129141
echo "Backing up Redis database ..."
130-
ghe-backup-redis > redis.rdb ||
131-
failures="$failures redis"
142+
if $cluster; then
143+
ghe-backup-redis-cluster > redis.rdb ||
144+
failures="$failures redis"
145+
else
146+
ghe-backup-redis > redis.rdb ||
147+
failures="$failures redis"
148+
fi
149+
150+
echo "Backing up audit log ..."
151+
ghe-backup-es-audit-log ||
152+
failures="$failures audit-log"
153+
154+
echo "Backing up hookshot logs ..."
155+
ghe-backup-es-hookshot ||
156+
failures="$failures hookshot"
132157

133158
echo "Backing up Git repositories ..."
134159
ghe-backup-repositories-${GHE_BACKUP_STRATEGY} ||
@@ -139,18 +164,30 @@ ghe-backup-pages-${GHE_BACKUP_STRATEGY} ||
139164
failures="$failures pages"
140165

141166
if [ "$GHE_VERSION_MAJOR" -ge 2 ]; then
142-
echo "Backing up asset attachments ..."
143-
ghe-backup-userdata alambic_assets ||
144-
failures="$failures alambic_assets"
145-
146-
echo "Backing up hook deliveries ..."
147-
ghe-backup-userdata hookshot ||
148-
failures="$failures hookshot"
167+
if $cluster; then
168+
echo "Backing up asset attachments ..."
169+
ghe-backup-alambic-cluster ||
170+
failures="$failures alambic_assets"
171+
else
172+
echo "Backing up asset attachments ..."
173+
ghe-backup-userdata alambic_assets ||
174+
failures="$failures alambic_assets"
175+
176+
echo "Backing up storage data ..."
177+
ghe-backup-userdata storage ||
178+
failures="$failures storage"
179+
180+
echo "Backing up hook deliveries ..."
181+
ghe-backup-userdata hookshot ||
182+
failures="$failures hookshot"
183+
fi
149184
fi
150185

151-
echo "Backing up Elasticsearch indices ..."
152-
ghe-backup-es-${GHE_BACKUP_STRATEGY} ||
153-
failures="$failures elasticsearch"
186+
if ! $cluster; then
187+
echo "Backing up Elasticsearch indices ..."
188+
ghe-backup-es-${GHE_BACKUP_STRATEGY} ||
189+
failures="$failures elasticsearch"
190+
fi
154191

155192
# If we're using the tarball backup strategy, bring the appliance out of
156193
# maintenance mode now instead of waiting until after pruning stale snapshots.

bin/ghe-host-check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ set -e
3737
if [ $rc -ne 0 ]; then
3838
case $rc in
3939
255)
40-
if echo "$output" | grep -i "port 22: connection refused" >/dev/null; then
40+
if echo "$output" | grep -i "port 22: connection refused\|Connection timed out during banner exchange" >/dev/null; then
4141
exec "bin/$(basename $0)" "$hostname:122"
4242
fi
4343

bin/ghe-restore

Lines changed: 68 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ elif [ "$GHE_VERSION_MAJOR" -ge 2 ]; then
9797
restore_settings=true
9898
fi
9999

100+
# Figure out if we're restoring into cluster
101+
cluster=false
102+
if ghe-ssh "$GHE_HOSTNAME" -- \
103+
"[ -f '$GHE_REMOTE_ROOT_DIR/etc/github/cluster' ]"; then
104+
cluster=true
105+
instance_configured=true
106+
restore_settings=false
107+
fi
108+
100109
# Figure out if this instance is in a replication pair
101110
if ghe-ssh "$GHE_HOSTNAME" -- "ghe-repl-status -r 2>/dev/null" \
102111
| grep -Eq "replica|primary"; then
@@ -176,40 +185,66 @@ fi
176185
# Make sure mysql and elasticsearch are prep'd and running before restoring into
177186
# appliances v2.x or greater. These services will not have been started on appliances
178187
# that have not been configured yet.
179-
if [ "$GHE_VERSION_MAJOR" -ge 2 ]; then
188+
if ! $cluster; then
189+
if [ "$GHE_VERSION_MAJOR" -ge 2 ]; then
180190
echo "sudo ghe-service-ensure-mysql && sudo ghe-service-ensure-elasticsearch" |
181191
ghe-ssh "$GHE_HOSTNAME" -- /bin/sh 1>&3
192+
fi
182193
fi
183194

184-
# Remove temporary 2.2 storage migration directory if it exists
185-
echo "if [ -d /data/user/repositories-nw-backup ]; then sudo rm -rf /data/user/repositories-nw-backup; fi" |
186-
ghe-ssh "$GHE_HOSTNAME" -- /bin/sh 1>&3
195+
echo "Restoring MySQL database ..."
196+
gzip -dc "$GHE_RESTORE_SNAPSHOT_PATH/mysql.sql.gz" | ghe-ssh "$GHE_HOSTNAME" -- 'ghe-import-mysql'
187197

188-
echo "Restoring Git repositories ..."
189-
ghe-restore-repositories-${GHE_BACKUP_STRATEGY} "$GHE_HOSTNAME" 1>&3
198+
echo "Restoring Redis database ..."
199+
ghe-ssh "$GHE_HOSTNAME" -- 'ghe-import-redis' < "$GHE_RESTORE_SNAPSHOT_PATH/redis.rdb" 1>&3
190200

191-
echo "Restoring GitHub Pages ..."
192-
ghe-restore-pages-${GHE_BACKUP_STRATEGY} "$GHE_HOSTNAME" 1>&3
201+
if $cluster; then
202+
echo "Restoring Git repositories into cluster ..."
203+
ghe-restore-repositories-dgit "$GHE_HOSTNAME" 1>&3
193204

194-
if [ "$GHE_VERSION_MAJOR" -ge 2 ]; then
195-
echo "Restoring asset attachments ..."
196-
ghe-restore-userdata alambic_assets "$GHE_HOSTNAME" 1>&3
205+
echo "Restoring Gists into cluster ..."
206+
ghe-restore-repositories-gist "$GHE_HOSTNAME" 1>&3
207+
else
208+
# Remove temporary 2.2 storage migration directory if it exists
209+
echo "if [ -d /data/user/repositories-nw-backup ]; then sudo rm -rf /data/user/repositories-nw-backup; fi" |
210+
ghe-ssh "$GHE_HOSTNAME" -- /bin/sh 1>&3
197211

198-
echo "Restoring hook deliveries ..."
199-
ghe-restore-userdata hookshot "$GHE_HOSTNAME" 1>&3
212+
echo "Restoring Git repositories ..."
213+
ghe-restore-repositories-${GHE_BACKUP_STRATEGY} "$GHE_HOSTNAME" 1>&3
200214
fi
201215

202-
echo "Restoring MySQL database ..."
203-
gzip -dc "$GHE_RESTORE_SNAPSHOT_PATH/mysql.sql.gz" | ghe-ssh "$GHE_HOSTNAME" -- 'ghe-import-mysql' 1>&3
204-
205-
echo "Restoring Redis database ..."
206-
ghe-ssh "$GHE_HOSTNAME" -- 'ghe-import-redis' < "$GHE_RESTORE_SNAPSHOT_PATH/redis.rdb" 1>&3
216+
if $cluster; then
217+
echo "Restoring GitHub Pages into DPages..."
218+
ghe-restore-pages-dpages "$GHE_HOSTNAME" 1>&3
219+
else
220+
echo "Restoring GitHub Pages ..."
221+
ghe-restore-pages-${GHE_BACKUP_STRATEGY} "$GHE_HOSTNAME" 1>&3
222+
fi
207223

208224
echo "Restoring SSH authorized keys ..."
209225
ghe-ssh "$GHE_HOSTNAME" -- 'ghe-import-authorized-keys' < "$GHE_RESTORE_SNAPSHOT_PATH/authorized-keys.json" 1>&3
210226

211-
echo "Restoring Elasticsearch indices ..."
212-
ghe-restore-es-${GHE_BACKUP_STRATEGY} "$GHE_HOSTNAME" 1>&3
227+
if $cluster; then
228+
echo "Restoring storage data ..."
229+
ghe-restore-alambic-cluster "$GHE_HOSTNAME" 1>&3
230+
elif [ "$GHE_VERSION_MAJOR" -ge 2 ]; then
231+
echo "Restoring asset attachments ..."
232+
ghe-restore-userdata alambic_assets "$GHE_HOSTNAME" 1>&3
233+
234+
echo "Restoring storage data ..."
235+
ghe-restore-userdata storage "$GHE_HOSTNAME" 1>&3
236+
237+
echo "Restoring hook deliveries ..."
238+
ghe-restore-userdata hookshot "$GHE_HOSTNAME" 1>&3
239+
fi
240+
241+
if $cluster; then
242+
echo "Restoring ElasticSearch Audit logs"
243+
ghe-restore-es-audit-log "$GHE_HOSTNAME" 1>&3
244+
else
245+
echo "Restoring Elasticsearch indices ..."
246+
ghe-restore-es-${GHE_BACKUP_STRATEGY} "$GHE_HOSTNAME" 1>&3
247+
fi
213248

214249
# Restart an already running memcached to reset the cache after restore
215250
if [ "$GHE_VERSION_MAJOR" -ge 2 ]; then
@@ -220,10 +255,13 @@ fi
220255

221256
# When restoring to a host that has already been configured, kick off a
222257
# config run to perform data migrations.
223-
if $instance_configured; then
258+
if $cluster; then
259+
echo "Configuring cluster ..."
260+
ghe-ssh "$GHE_HOSTNAME" -- "ghe-cluster-config-apply" 1>&3 2>&3
261+
elif $instance_configured; then
224262
echo "Configuring storage ..."
225263
if [ "$GHE_VERSION_MAJOR" -ge 2 ]; then
226-
ghe-ssh "$GHE_HOSTNAME" -- "sudo ghe-config-apply --full" 1>&3
264+
ghe-ssh "$GHE_HOSTNAME" -- "ghe-config-apply --full" 1>&3 2>&3
227265
else
228266
echo " This will take several minutes to complete..."
229267
ghe-ssh "$GHE_HOSTNAME" -- "sudo enterprise-configure" 1>&3 2>&3
@@ -239,8 +277,13 @@ update_restore_status "complete"
239277
# Log restore complete message in /var/log/syslog on remote instance
240278
ghe_remote_logger "Completed restore from $(hostname) / snapshot ${GHE_SNAPSHOT_TIMESTAMP}."
241279

242-
echo "Restoring SSH host keys ..."
243-
ghe-ssh "$GHE_HOSTNAME" -- 'ghe-import-ssh-host-keys' < "$GHE_RESTORE_SNAPSHOT_PATH/ssh-host-keys.tar" 1>&3
280+
if ! $cluster; then
281+
echo "Restoring SSH host keys ..."
282+
ghe-ssh "$GHE_HOSTNAME" -- 'ghe-import-ssh-host-keys' < "$GHE_RESTORE_SNAPSHOT_PATH/ssh-host-keys.tar" 1>&3
283+
fi
244284

245285
echo "Completed restore of $GHE_HOSTNAME from snapshot $GHE_RESTORE_SNAPSHOT"
246-
echo "Visit https://$hostname/setup/settings to review appliance configuration."
286+
287+
if ! $cluster; then
288+
echo "Visit https://$hostname/setup/settings to review appliance configuration."
289+
fi

debian/changelog

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
github-backup-utils (2.5.0) UNRELEASED; urgency=medium
2+
3+
* Adds GitHub Enterpise 2.5 support
4+
* Adds GitHub Enterprise Cluster support
5+
* Backups and restores SAML keypairs
6+
7+
-- Sergio Rubio <[email protected]> Tue, 9 Feb 2016 00:02:37 +0000
8+
19
github-backup-utils (2.4.0) UNRELEASED; urgency=medium
210

311
* Moves the in-progress detection to a separate file with PID which is
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/bin/sh
2+
#/ Usage: ghe-backup-alambic-cluster
3+
#/ Take an online, incremental snapshot of all Alambic Storage data
4+
#/
5+
#/ Note: This command typically isn't called directly. It's invoked by
6+
#/ ghe-backup when the cluster strategy is used.
7+
set -e
8+
9+
# Bring in the backup configuration
10+
cd $(dirname "$0")/../..
11+
. share/github-backup-utils/ghe-backup-config
12+
13+
# Set up remote host and root backup snapshot directory based on config
14+
host="$GHE_HOSTNAME"
15+
backup_dir="$GHE_SNAPSHOT_DIR/storage"
16+
17+
# Verify rsync is available.
18+
if ! rsync --version 1>/dev/null 2>&1; then
19+
echo "Error: rsync not found." 1>&2
20+
exit 1
21+
fi
22+
23+
# Perform a host-check and establish GHE_REMOTE_XXX variables.
24+
ghe_remote_version_required "$host"
25+
26+
# Generate SSH config for forwarding
27+
28+
config=""
29+
30+
# Split host:port into parts
31+
port=$(ssh_port_part "$GHE_HOSTNAME")
32+
host=$(ssh_host_part "$GHE_HOSTNAME")
33+
34+
# Add user / -l option
35+
user="${host%@*}"
36+
[ "$user" = "$host" ] && user="admin"
37+
38+
# git server hostnames
39+
hostnames=$(ghe_cluster_online_nodes "storage-server")
40+
41+
for hostname in $hostnames; do
42+
config="$config
43+
Host $hostname
44+
ProxyCommand ssh -q $GHE_EXTRA_SSH_OPTS -p $port $user@$host nc.openbsd %h %p
45+
StrictHostKeyChecking=no
46+
"
47+
done
48+
49+
config_file=$(mktemp -t cluster-backup-restore-XXXXXX)
50+
echo "$config" > "$config_file"
51+
52+
opts="$GHE_EXTRA_SSH_OPTS -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o PasswordAuthentication=no"
53+
54+
# Make sure root backup dir exists if this is the first run
55+
mkdir -p "$backup_dir"
56+
57+
# Removes the remote sync-in-progress file on exit, re-enabling GC operations
58+
# on the remote instance.
59+
cleanup() {
60+
rm -f $config_file
61+
}
62+
trap 'cleanup' EXIT INT
63+
64+
# If we have a previous increment and it is not empty, avoid transferring existing files via rsync's
65+
# --link-dest support. This also decreases physical space usage considerably.
66+
if [ -d "$GHE_DATA_DIR/current/storage" ] && [ "$(ls -A $GHE_DATA_DIR/current/storage)" ]; then
67+
link_dest="--link-dest=../../current/storage"
68+
fi
69+
70+
for hostname in $hostnames; do
71+
echo 1>&3
72+
echo "* Starting backup for host: $hostname"
73+
# Sync all auxiliary repository data. This includes files and directories like
74+
# HEAD, audit_log, config, description, info/, etc. No refs or object data
75+
# should be transferred here.
76+
echo 1>&3
77+
echo "* Transferring storage files ..." 1>&3
78+
79+
# Transfer all data from the user data directory using rsync.
80+
ghe-rsync -az \
81+
-e "ssh -q $opts -p 122 -F $config_file -l $user" \
82+
--rsync-path='sudo -u git rsync' \
83+
$link_dest \
84+
"$hostname:$GHE_REMOTE_DATA_USER_DIR/storage/" \
85+
"$GHE_SNAPSHOT_DIR/storage" 1>&3
86+
done

share/github-backup-utils/ghe-backup-config

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ GHE_SNAPSHOT_DIR="$GHE_DATA_DIR"/"$GHE_SNAPSHOT_TIMESTAMP"
130130
# Set "true" to get verbose logging of all ssh commands on stderr
131131
: ${GHE_VERBOSE_SSH:=false}
132132

133+
# The location of the cluster configuration file file on the remote side.
134+
# This is always "/data/user/common/cluster.conf" for GitHub Cluster instances.
135+
# Use of this variable allows the location to be overridden in tests.
136+
: ${GHE_REMOTE_CLUSTER_CONF_FILE:="$GHE_REMOTE_DATA_DIR/user/common/cluster.conf"}
133137

134138
###############################################################################
135139
### Dynamic remote version config
@@ -241,3 +245,10 @@ ghe_remote_logger () {
241245
echo "$@" |
242246
ghe-ssh "$GHE_HOSTNAME" -- logger -t backup-utils || true
243247
}
248+
249+
# Usage: ghe_cluster_online_nodes role
250+
# Returns the online nodes with a certain role in cluster
251+
ghe_cluster_online_nodes () {
252+
role=$1
253+
echo "ghe-config --get-regexp cluster.*.$role | egrep 'true$' | awk '{ print \$1; }' | awk 'BEGIN { FS=\".\" }; { print \$2 };' | xargs -I{} -n1 bash -c 'if [ \"\$(ghe-config cluster.\$hostname.offline)\" != true ]; then ghe-config cluster.{}.hostname; fi'" | ghe-ssh "$GHE_HOSTNAME" /bin/bash
254+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/bin/sh
2+
#/ Usage: ghe-backup-es-audit-log
3+
#/ Take a backup of audit logs in ElasticSearch.
4+
#/
5+
#/ Note: This command typically isn't called directly. It's invoked by
6+
#/ ghe-backup.
7+
set -e
8+
9+
# Bring in the backup configuration
10+
cd $(dirname "$0")/../..
11+
. share/github-backup-utils/ghe-backup-config
12+
13+
# Set up remote host and root elastic backup directory based on config
14+
host="$GHE_HOSTNAME"
15+
16+
# Perform a host-check and establish GHE_REMOTE_XXX variables.
17+
ghe_remote_version_required "$host"
18+
19+
# Make sure root backup dir exists if this is the first run
20+
mkdir -p "$GHE_SNAPSHOT_DIR/audit-log"
21+
22+
indices=$(ghe-ssh "$host" 'curl -s "localhost:9201/_cat/indices/audit_log*"' | cut -d ' ' -f 3)
23+
current_index=audit_log-$(ghe-ssh "$host" 'date +"%Y-%m"')
24+
25+
for index in $indices; do
26+
if [ -f $GHE_DATA_DIR/current/audit-log/$index.gz -a $index \< $current_index ]; then
27+
# Hard link any older indices since they are read only and won't change
28+
ln $GHE_DATA_DIR/current/audit-log/$index.gz $GHE_SNAPSHOT_DIR/audit-log/$index.gz
29+
else
30+
ghe-ssh "$host" "/usr/local/share/enterprise/ghe-es-dump-json 'http://localhost:9201/$index'" | gzip > $GHE_SNAPSHOT_DIR/audit-log/$index.gz
31+
fi
32+
done

0 commit comments

Comments
 (0)