#!/usr/local/bin/bash
#/ Usage: ghe-restore-repositories-dgit-ng <host>
#/ Restore repositories fron an rsync snapshot of all Git repository data to a GitHub cluster.
#/
#/ Note: This script typically isn't called directly. It's invoked by the
#/ ghe-restore command when restoring into a cluster.
set -e

sync_repo_info() {
  # node names changed in 2.8 and `ghe-cluster-each -u` isn't available
  # on GHE <= 2.7.X. We need to be backwards compatible.
  if ghe-ssh "$GHE_HOSTNAME" test -f /data/user/common/uuid; then
    for uuid in `ghe-ssh "$GHE_HOSTNAME" ghe-cluster-each -r git -u`; do
      if ! ghe-rsync -av --delete \
          -e "ssh -q $opts -p $port -F $ssh_config_file -l $user" \
          --rsync-path="sudo -u git rsync" \
          "$GHE_DATA_DIR/$GHE_RESTORE_SNAPSHOT/repositories/info/" \
          "git-server-$uuid:$GHE_REMOTE_DATA_USER_DIR/repositories/info" 1>&3; then
        echo "Error restoring /data/repositories/info to git-server-$uuid" 1>&2
      fi
    done
  else
    for route in `ghe-ssh "$GHE_HOSTNAME" ghe-cluster-each -r git -p`; do
      if ! ghe-rsync -av --delete \
          -e "ssh -q $opts -p $port -F $ssh_config_file -l $user" \
          --rsync-path="sudo -u git rsync" \
          "$GHE_DATA_DIR/$GHE_RESTORE_SNAPSHOT/repositories/info/" \
          "$route:$GHE_REMOTE_DATA_USER_DIR/repositories/info" 1>&3; then
        echo "Error restoring /data/repositories/info to $route" 1>&2
      fi
    done
  fi
}

# Bring in the backup configuration
. $( dirname "${BASH_SOURCE[0]}" )/ghe-backup-config

# Show usage and bail with no arguments
[ -z "$*" ] && print_usage

bm_start "$(basename $0)"

# Grab host arg
GHE_HOSTNAME="$1"

# The snapshot to restore should be set by the ghe-restore command but this lets
# us run this script directly.
: ${GHE_RESTORE_SNAPSHOT:=current}

network_paths=$(cd $GHE_DATA_DIR/$GHE_RESTORE_SNAPSHOT/ && find repositories -mindepth 6 -maxdepth 7 -name \*.git -exec dirname {} \; | uniq | grep nw | cut -d / -f2-)

if [ -z "$network_paths" ]; then
  echo "Warning: Repositories backup missing. Skipping ..."
  exit 0
fi

# Perform a host-check and establish GHE_REMOTE_XXX variables.
ghe_remote_version_required "$GHE_HOSTNAME"

# Generate SSH config for forwarding
# Split host:port into parts
port=$(ssh_port_part "$GHE_HOSTNAME")
host=$(ssh_host_part "$GHE_HOSTNAME")

# Add user / -l option
user="${host%@*}"
[ "$user" = "$host" ] && user="admin"

tempdir=$(mktemp -d)
ssh_config_file=$tempdir/ssh_config
opts="$GHE_EXTRA_SSH_OPTS -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o PasswordAuthentication=no"
tmp_list=$tempdir/tmp_list
to_restore=$tempdir/to_restore

hostnames=$(ghe-cluster-hostnames "$GHE_HOSTNAME" "git-server")
for hostname in $hostnames; do
  echo "
Host $hostname
  ServerAliveInterval 60
  ProxyCommand ssh -q $GHE_EXTRA_SSH_OPTS -p $port $user@$host nc.openbsd %h %p
  StrictHostKeyChecking=no" >> $ssh_config_file
done

cleanup() {
  for hostname in $hostnames; do
    ghe-gc-enable -F $ssh_config_file $hostname:$port
  done
  rm -rf $tempdir
}
trap cleanup EXIT

# Disable remote GC operations
for hostname in $hostnames; do
  ghe-gc-disable -F $ssh_config_file $hostname:$port
done

# Build a list of network paths to send to the server to calculate
# the restore routes, something like:
#
# a/nw/a8/3f/02/100000855
# a/nw/a8/bc/8d/100000880
# a/nw/a5/06/81/100000659
# a/nw/a5/84/6f/100000708
# a/nw/a5/e0/01/146
# ...
#
# One network path per line.
OLDIFS=$IFS; IFS=$'\n'
for path in $network_paths; do
   # Get the network ID
   # The nework id from a repository is the last component of the path
   # i.e. /data/repositories/a/nw/a5/bf/c9/37 network ID would be 37
   ghe_verbose "Adding network_path $path to the list of networks to send"
   echo $path
done > $tmp_list
IFS=$OLDIFS

# The server returns a list of routes:
#
# a/nw/a8/3f/02/100000855 dgit-node1 dgit-node2 dgit-node3
# a/nw/a8/bc/8d/100000880 dgit-node1 dgit-node2 dgit-node4
# a/nw/a5/06/81/100000659 dgit-node3 dgit-node2 dgit-node4
# ...
#
# One route per line.
cat $tmp_list | ghe-ssh "$GHE_HOSTNAME" github-env ./bin/dgit-cluster-restore-routes \
 | while read route; do
  ghe_verbose "Received route $route"
  servers=$(echo $route | cut -d ' ' -f2-)
  for server in $servers; do
    network_path=$(echo $route | cut -d ' ' -f1)
    ghe_verbose "Adding $network_path to $tempdir/$server.rsync"
    echo "$network_path" >> $tempdir/$server.rsync
  done

  network_id=$(echo $network_path | awk -F/ '{print $(NF)}')
  ghe_verbose "Route: $network_id /data/repositories/$network_path $servers"
  echo "$network_id /data/repositories/$network_path $servers" >> $to_restore
done

# rsync all the repositories
for file_list in $tempdir/*.rsync; do
  server=$(basename $file_list .rsync)
  ghe_verbose "* Transferring repositories to $server"
  ghe-rsync -avrHR --delete \
    -e "ssh -q $opts -p $port -F $ssh_config_file -l $user" \
    --rsync-path="sudo -u git rsync" \
    --files-from=$file_list \
    "$GHE_DATA_DIR/$GHE_RESTORE_SNAPSHOT/repositories/./" \
    "$server:$GHE_REMOTE_DATA_USER_DIR/repositories/" 1>&3
done

# Tell dgit about the repositories restored
cat $to_restore | ghe-ssh "$GHE_HOSTNAME" github-env ./bin/dgit-cluster-restore-finalize >&3

if [ -d $GHE_DATA_DIR/$GHE_RESTORE_SNAPSHOT/repositories/info ]; then
  ghe_verbose "* Transferring repository info data"
  sync_repo_info
else
  ghe_verbose "* Removing repository info data"
  ghe-ssh "$GHE_HOSTNAME" ghe-cluster-each -r git -- rm -f /data/repositories/info/*
fi

bm_end "$(basename $0)"
