#!/bin/bash
# Shorter interface to deploy scripts on servers.
# Bash script because it uses temporary named pipes <(cmd)

# set -x  # debugging

SCRIPT_LOCATION=$(dirname $(readlink -f $0))
SCRIPT_COMMAND=$(basename $(readlink -f $0))

source $SCRIPT_LOCATION/../shlib/deployfns

if [ x$1 = x ] ; then 
    die "specify a command (try --help for help)"
fi

# Ensure local /data/vhosts.json is up-to-date
/data/mysociety/bin/update-vhosts-json

show_help () {
	cat <<END
Usage: mysociety COMMAND [OPTIONS]

COMMAND is one of:
    config
        Tell Puppet to pull new manifests and apply them

    vhost [--server server | --all] VHOST [--force] [--flush]
	    Deploy latest version of VHOST, e.g. www.pledgebank.com,
        optionally on [server] or, with --all, on all VHOST's servers
        --force will override any local uncommitted changes
        --flush will ban VHOST and its aliases from local Varnish

    vhost stop/update/start [--server server | --all] VHOST
        Stop/update/start VHOST, e.g. www.pledgebank.com

    vhost remove VHOST
        Remove crontab, Apache config, email forwards etc. for VHOST

    vhost servers VHOST
        Print which servers VHOST is deployed on

    test VHOST
        Deploy, then run test script on VHOST, e.g. testharness.pet.mysociety.org
        It will try VHOST.test.mysociety.org if no "." is present.

    devhost [stop/update/start/remove] SITE
        When run with sudo, same as mysociety vhost <username>.SITE.dev.mysociety.org

    dns --all
        Update the DNS configuration on all hosts for which the Puppet
        fact `is_nameserver` is true.

    email
        Update the exim virtual mail configuration on the current host

    email --all
        Update the exim virtual mail configuration on the all hosts
        managed by Puppet.

    commit
        git commit and git push /data/servers

    diff
        diff /data/servers and /data/mysociety

    base COMMAND
        Run a command on all machines known to Puppet

    base -l
        List all servers known to Puppet

    debug
    	Show debugging information for mysociety command

Extra parameters are passed through to underlying deploy-... script.

Set MYSOCIETY_DEBUG=1 to enable extra debugging (currently this just shows "shell"
commands that the mySociety Perl scripts call).

END
	exit
}

if [ $1 = "--help" ] ; then
    show_help
fi

HOST=$(hostname)

if [ x$HOST = x ] ; then
    die "can't establish hostname"
fi

COMMAND=$1

# This should always shift now, as we're checking for empty $1 at the top
shift || die "specify a command (try --help for help)"

set -e

case $COMMAND in
    config)
        # if a puppet agent is (or should be) running, force it to fetch new configs
        # (and don't use the old configuration system)
        set +e
        /usr/sbin/service puppet status >/dev/null 2>&1
        if [ "$?" = 0 ]; then
            if [ -r '/var/run/puppetlabs/agent.pid' ] ; then
                kill -USR1 "`cat /var/run/puppetlabs/agent.pid`"
                echo "Signalled puppet to fetch new manifests"
            else
                die "puppet agent running, but pid file can't be read"
            fi
        else
            die "puppet agent expected but not running"
        fi
        set -e
        ;;
        
    vhost)
        COMMAND=deploy
        VHOST=$1
        DEPLOYMENT_ID=${DEPLOYMENT_ID:-$(uuidgen | tr -d '\n')}
        shift || die "specify a command or a virtual host"
        if [ "$VHOST" == "stop" -o "$VHOST" == "update" -o "$VHOST" == "start" -o "$VHOST" == "remove" -o "$VHOST" == "servers" -o "$VHOST" == "balancers" ]
        then
            COMMAND=$VHOST
            VHOST=$1
            shift || die "specify a virtual host"
        fi

        USE_PACKAGE_FROM_CONFIG=$(jq .vhosts.\"${VHOST}\".use_package /data/vhosts.json 2>/dev/null)
        USE_PACKAGE=${USE_PACKAGE-$USE_PACKAGE_FROM_CONFIG}
        if [ "$VHOST" == "--all" ]
        then
            VHOST=$1
            # Varnish doesn't like backends to start with numbers
            # So where a vhost starts with a number we prefix a "b".
            if [[ "$VHOST" =~ ^[0-9] ]]; then
                START_CHAR="b"
            else
                START_CHAR=""
            fi
            shift || die "specify a vhost"
            # Deal with non-sensical commands combined with --all
            if [ "$COMMAND" == "servers" ] || [  "$COMMAND" == "balancers" ]; then die "Using ${COMMAND} with --all makes no sense."; fi
            if pgrep -f "\--all[ ]*$VHOST" | grep -v $$ | grep -v $PPID >/dev/null; then die "Someone is currently already running this."; fi
            THREAD_ID=$(/data/mysociety/bin/deploy-logger "Performing ${COMMAND} for ${VHOST} on all servers")
            if [ "$COMMAND" == "deploy" ]; then COMMAND=""; fi
            BALANCERS=$(mysociety vhost balancers "$VHOST")
            SERVERS=$(mysociety vhost servers "$VHOST")
            SERVERS_INTERNAL=$(jq -r -c .vhosts.\"${VHOST}\".servers_internal[]? /data/vhosts.json 2>/dev/null)
            NUM_INTERNAL_SERVERS=$(echo $SERVERS_INTERNAL | wc -w)
            NUM_WEB_SERVERS=$(($(echo $SERVERS | wc -w) - $NUM_INTERNAL_SERVERS))
            for s in $SERVERS; do
                # Work out if we want to remove this server from the load balancers.
                # We only want to do this is there is more than one public-facing
                # web server and this server is one of those systems.
                DO_VARNISH="yes"
                if [ "$NUM_WEB_SERVERS" -eq "1" ]; then
                    DO_VARNISH='no'
                elif [ -n "$SERVERS_INTERNAL" ] ; then
                    for si in $SERVERS_INTERNAL ; do
                        if [ "$s" == "$si" ] ; then
                            DO_VARNISH=no
                        fi
                    done
                fi
                VHOST_BACKEND=$(echo "${START_CHAR}${VHOST}_${s}" | sed -e 's/\./_/g')
                if [ -n "$BALANCERS" ] && [ "$DO_VARNISH" == "yes" ]; then
                    # remove server from balancers if there are balancers and more than one server...
                    for b in $BALANCERS; do
                        PORT=$(jq --arg lb "$(echo $b | cut -d. -f1)" -r '.[$lb]' /etc/mysociety/varnishadm.json)
                        echo -e "\033[34m[deploy] Removing ${VHOST} on ${s} from balancer ${b} via localhost:${PORT}...\033[0m"
                        sudo varnishadm -S /etc/varnish/secret -T localhost:${PORT} backend.set_health boot.${VHOST_BACKEND} sick
                    done
                    sleep 5
                fi
                echo -e "\033[34m[deploy] Performing ${COMMAND:-deploy} for ${VHOST} on ${s}...\033[0m"
                ssh -t "$s" sudo env ${USE_PACKAGE:+USE_PACKAGE="$USE_PACKAGE"} DEPLOYMENT_ID="$DEPLOYMENT_ID" mysociety vhost "$COMMAND" "$VHOST" "$@" --thread $THREAD_ID
                if [ "$COMMAND" != "stop" ] && [ "$COMMAND" != "remove" ]; then
                    if [ -n "$BALANCERS" ] && [ "$DO_VARNISH" == "yes" ]; then
                        # This should provide a bit of time for process manager to start, or at least have the
                        # probe mark the back-end as sick before we switch back to auto - otherwise we sometimes
                        # see a couple of 503 responses before this happens.
                        sleep 10
                        # Switch back to Auto, and if starting/deploying wait until healthy.
                        for b in $BALANCERS; do
                            PORT=$(jq --arg lb "$(echo $b | cut -d. -f1)" -r '.[$lb]' /etc/mysociety/varnishadm.json)
                            echo -e "\033[34m[deploy] Adding ${VHOST} on ${s} to balancer ${b} via localhost:${PORT}..."
                            sudo varnishadm -S /etc/varnish/secret -T localhost:${PORT} backend.set_health boot.${VHOST_BACKEND} auto
                            # This check will ensure we don't start the next leg of the deploy
                            # until the instance is healthy on both load balancers.
                            until varnishadm -S /etc/varnish/secret -T localhost:${PORT} backend.list boot.${VHOST_BACKEND} | tail -n +2 | grep -iq Healthy
                            do
                                echo -n "."
                                sleep 1
                            done
                            echo -e "[deploy] ${VHOST} on ${s} is healthy - done.\033[0m"
                        done
                    fi
                fi
            done
            exit
        fi
        if [ "$VHOST" == "--server" ]
        then
            SERVER=$1
            shift || die "specify a server"
            VHOST=$1
            shift || die "specify a vhost"
            if [ "$COMMAND" == "deploy" ]; then COMMAND=""; fi
            for s in $SERVER; do
                ssh -t "$s" sudo env ${USE_PACKAGE:+USE_PACKAGE="$USE_PACKAGE"} DEPLOYMENT_ID="$DEPLOYMENT_ID" mysociety vhost "$COMMAND" "$VHOST" "$@"
            done
            exit
        fi
        # deploy vhost
        env ${USE_PACKAGE:+USE_PACKAGE="$USE_PACKAGE"} DEPLOYMENT_ID="$DEPLOYMENT_ID" /data/mysociety/bin/deploy-vhost $VHOST $COMMAND "$@"
        ;;

    devhost)
        COMMAND=
        SITE=$1
        shift || die "specify a command or a virtual host"
        if [ "$SITE" == "stop" -o "$SITE" == "update" -o "$SITE" == "start" -o "$SITE" == "remove" ]
        then
            COMMAND=$SITE
            SITE=$1
            shift || die "specify a virtual host"
        fi
        exec /data/mysociety/bin/mysociety vhost $COMMAND $SUDO_USER.$SITE.dev.mysociety.org "$@"
        ;;

    test) 
        VHOST=$1
        shift || die "specify a virtual host"
        if [[ $VHOST != *.* ]]; then
            VHOST=$VHOST.test.mysociety.org
        fi
        # update code (which also does apachectl graceful, so killing
        # any FastCGI processes holding the database open, so the test script
        # can drop and reload the database)
        /data/mysociety/bin/deploy-vhost $VHOST deploy "$@"
        # sleep for a few seconds to allow the Apache processes mentioned above to die
        sleep 10
        # launch test script
        VERBOSE=1 /data/mysociety/bin/test-site $VHOST
        ;;

    dns)
        if [ "$1" = "--all" ] ; then
            THREAD_ID=$(/data/mysociety/bin/deploy-logger "Deploying DNS on all servers")
            for srv in $(puppet query 'nodes [certname] { facts { name = "is_nameserver" and value = true } }' | jq -r .[].certname); do
                echo " -- on ${srv}..."
                ssh $srv "/data/mysociety/bin/mysociety dns --quiet --thread $THREAD_ID" || echo " -- ...failed"
            done
        else
            exec /data/mysociety/bin/deploy-dns "$@"
        fi
        ;;

    email)
        if [ "$1" = "--all" ] ; then
            THREAD_ID=$(/data/mysociety/bin/deploy-logger "Deploying email on all servers")
            for srv in $(puppet query 'nodes [certname] { facts { name = "is_mailserver" and value = true } }' | jq -r .[].certname); do
                echo " -- on ${srv}..."
                ssh $srv "/data/mysociety/bin/mysociety email --quiet --thread $THREAD_ID" || echo " -- ...failed"
            done
        else
            exec /data/mysociety/bin/deploy-email "$@"
        fi
        ;;

    commit)
        cd /data/servers
        git commit -q --allow-empty-message
        git push -q origin master
        ;;

    diff)
        cd /data/servers
        git diff -u
        cd /data/mysociety
        git diff -u
        ;;

    base)
        exec /data/mysociety/bin/mysociety all "$@"
        ;;

    all)
        for srv in $(/usr/local/bin/get-nodes); do
            if [ "$1" = '-l' ] ; then
                echo $srv
            else
                echo " -- on ${srv}..."
                ssh $srv "$@" || echo " -- ...failed"
            fi
        done
        ;;

    debug)
        echo "HOST: $HOST"
        ;;

    *)
        show_help
        ;;
esac
