#!/bin/sh
#/ Usage: ghe-restore [-v] [-s <snapshot-id>] [<host>]
#/ Restores a GitHub instance from local backup snapshots. The <host> is the
#/ hostname or IP of the GitHub instance. The <host> may be omitted when
#/ the GHE_RESTORE_HOST config variable is set in backup.config. When a <host>
#/ argument is provided, it always overrides the configured restore host.
#/
#/ Options:
#/   -f                Don't prompt for confirmation before restoring.
#/   -c                Restore appliance settings and license in addition to
#/                     datastores. Settings are not restored by default to
#/                     prevent overwriting different configuration on the
#/                     restore host.
#/   -s <snapshot-id>  Restore from the snapshot with the given id. Available
#/                     snapshots may be listed under the data directory.
#/   -v                Enable verbose output.
#/
#/ Note that the host must be reachable and your SSH key must be setup as
#/ described in the following help article:
#/
#/ <https://enterprise.github.com/help/articles/adding-an-ssh-key-for-shell-access>
set -e

# Bring in the backup configuration.
cd $(dirname "$0")/..
. share/github-backup-utils/ghe-backup-config

# Parse arguments
restore_settings=false
force=false
while true; do
    case "$1" in
        -f|--force)
            force=true
            shift
            ;;
        -s)
            snapshot_id="$(basename "$2")"
            shift 2
            ;;
        -c)
            restore_settings=true
            shift
            ;;
        -*)
            echo "Error: invalid argument: '$1'" 1>&2
            exit 1
            ;;
        *)
            break
            ;;
    esac
done

# Grab the host arg
GHE_HOSTNAME="${1:-$GHE_RESTORE_HOST}"

# Hostname without any port suffix
hostname=$(echo "$GHE_HOSTNAME" | cut -f 1 -d :)

# Show usage with no <host>
[ -z "$GHE_HOSTNAME" ] && print_usage

# ghe-restore-snapshot-path validates it exists, determines what current is,
# and if there's any problem, exit for us
GHE_RESTORE_SNAPSHOT_PATH="$(ghe-restore-snapshot-path "$snapshot_id")"
GHE_RESTORE_SNAPSHOT=$(basename "$GHE_RESTORE_SNAPSHOT_PATH")
export GHE_RESTORE_SNAPSHOT

# Figure out whether to use the tarball or rsync restore strategy based on the
# strategy file written in the snapshot directory.
GHE_BACKUP_STRATEGY=$(cat "$GHE_RESTORE_SNAPSHOT_PATH/strategy")

# Perform a host-check and establish the remote version in GHE_REMOTE_VERSION.
ghe_remote_version_required "$GHE_HOSTNAME"

# Keep other processes on the VM in the loop about the restore status.
#
# Other processes will look for these states:
# "restoring" - restore is currently in progress
# "failed"    - restore has failed
# "complete"  - restore has completed successfully
update_restore_status () {
    if [ "$GHE_VERSION_MAJOR" -ge 2 ]; then
        echo "$1" |
        ghe-ssh "$GHE_HOSTNAME" -- "sudo dd of='$GHE_REMOTE_DATA_USER_DIR/common/ghe-restore-status' 2>/dev/null"
    fi
}

# Figure out if this instance has been configured or is entirely new.
instance_configured=false
if ghe-ssh "$GHE_HOSTNAME" -- \
    "[ -f '$GHE_REMOTE_DATA_DIR/enterprise/dna.json' -o \
       -f '$GHE_REMOTE_ROOT_DIR/etc/github/configured' ]"; then
    instance_configured=true
elif [ "$GHE_VERSION_MAJOR" -ge 2 ]; then
    restore_settings=true
fi

# Figure out if this instance is in a replication pair
if ghe-ssh "$GHE_HOSTNAME" -- "ghe-repl-status -r 2>/dev/null" \
  | grep -Eq "replica|primary"; then
  instance_configured=true
  echo "WARNING: Restoring to a server with replication enabled interrupts replication."
  echo "         You will need to reconfigure replication after the restore completes."
fi

# Prompt to verify the restore host given is correct. Restoring overwrites
# important data on the destination appliance that cannot be recovered. This is
# mostly to prevent accidents where the backup host is given to restore instead
# of a separate restore host since they're used in such close proximity.
if $instance_configured && ! $force; then
    echo
    echo "WARNING: All data on GitHub Enterprise appliance $hostname ($GHE_REMOTE_VERSION)"
    echo "         will be overwritten with data from snapshot ${GHE_RESTORE_SNAPSHOT}."
    echo "Please verify that this is the correct restore host before continuing."
    printf "Type 'yes' to continue: "

    while read -r response; do
        case $response in
            yes|Yes|YES)
                break
                ;;
            '')
                printf "Type 'yes' to continue: "
                ;;
            *)
                echo "Restore aborted." 1>&2
                exit 1
                ;;
        esac
    done
    echo
fi

echo "Starting restore of $GHE_HOSTNAME from snapshot $GHE_RESTORE_SNAPSHOT"
trap "update_restore_status failed" EXIT
update_restore_status "restoring"

# Verify the host has been fully configured at least once if when running
# against v11.10.x appliances and the -c option wasn't specified.
if [ "$GHE_VERSION_MAJOR" -le 1 ] && ! $restore_settings && ! $instance_configured; then
    echo "Error: $hostname not configured." 1>&2
    echo "Please visit https://$hostname/setup/settings to configure base appliance settings before continuing." 1>&2
    exit 1
fi

# Restoring Elasticsearch to 11.10.3x via rsync requires GNU tar
if [ "$GHE_VERSION_MAJOR" -le 1 ] && [ "$GHE_BACKUP_STRATEGY" = "rsync" ]; then
    if ! tar --version | grep GNU >/dev/null; then
        if ! command -v gtar >/dev/null 2>&1; then
            echo "GNU tar is required.  Aborting." >&2
            exit 1
        fi
    fi
fi

# Make sure the GitHub appliance is in maintenance mode.
if $instance_configured; then
    if ! ghe-maintenance-mode-status "$GHE_HOSTNAME"; then
        echo "Error: $GHE_HOSTNAME must be put in maintenance mode before restoring. Aborting." 1>&2
        exit 1
    fi
fi

# Restore settings and license if restoring to an unconfigured appliance or when
# specified manually.
if $restore_settings; then
    ghe-restore-settings "$GHE_HOSTNAME"
fi

# Make sure mysql and elasticsearch are prep'd and running before restoring into
# appliances v2.x or greater. These services will not have been started on appliances
# that have not been configured yet.
if [ "$GHE_VERSION_MAJOR" -ge 2 ]; then
    echo "sudo ghe-service-ensure-mysql && sudo ghe-service-ensure-elasticsearch" |
    ghe-ssh "$GHE_HOSTNAME" -- /bin/sh 1>&3
fi

echo "Restoring Git repositories ..."
ghe-restore-repositories-${GHE_BACKUP_STRATEGY} "$GHE_HOSTNAME" 1>&3

echo "Restoring GitHub Pages ..."
ghe-restore-pages-${GHE_BACKUP_STRATEGY} "$GHE_HOSTNAME" 1>&3

if [ "$GHE_VERSION_MAJOR" -ge 2 ]; then
    echo "Restoring asset attachments ..."
    ghe-restore-userdata alambic_assets "$GHE_HOSTNAME" 1>&3

    echo "Restoring hook deliveries ..."
    ghe-restore-userdata hookshot "$GHE_HOSTNAME" 1>&3
fi

echo "Restoring MySQL database ..."
gzip -dc "$GHE_RESTORE_SNAPSHOT_PATH/mysql.sql.gz" | ghe-ssh "$GHE_HOSTNAME" -- 'ghe-import-mysql' 1>&3

echo "Restoring Redis database ..."
ghe-ssh "$GHE_HOSTNAME" -- 'ghe-import-redis' < "$GHE_RESTORE_SNAPSHOT_PATH/redis.rdb" 1>&3

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

echo "Restoring Elasticsearch indices ..."
ghe-restore-es-${GHE_BACKUP_STRATEGY} "$GHE_HOSTNAME" 1>&3

# When restoring to a host that has already been configured, kick off a
# config run to perform data migrations.
if $instance_configured; then
  echo "Configuring storage ..."
  if [ "$GHE_VERSION_MAJOR" -ge 2 ]; then
    ghe-ssh "$GHE_HOSTNAME" -- "sudo ghe-config-apply --full" 1>&3
  else
    echo "   This will take several minutes to complete..."
    ghe-ssh "$GHE_HOSTNAME" -- "sudo enterprise-configure" 1>&3 2>&3
  fi
fi

# Update the remote status to "complete". This has to happen before importing
# ssh host keys because subsequent commands will fail due to the host key
# changing otherwise.
trap "" EXIT
update_restore_status "complete"

echo "Restoring SSH host keys ..."
ghe-ssh "$GHE_HOSTNAME" -- 'ghe-import-ssh-host-keys' < "$GHE_RESTORE_SNAPSHOT_PATH/ssh-host-keys.tar" 1>&3

echo "Completed restore of $GHE_HOSTNAME from snapshot $GHE_RESTORE_SNAPSHOT"
echo "Visit https://$hostname/setup/settings to review appliance configuration."
