#! /usr/bin/env bash

# This script starts GPUdb's MQ (High Availability) module.

DATETIME=$(date '+%Y-%m-%d_%H.%M.%S')

# The directory of this script.
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

if ! source "$SCRIPT_DIR/gpudb_bash_utils.sh" ; then
    echo "Error: Unable to source: '$SCRIPT_DIR/gpudb_bash_utils.sh' from: '${BASH_SOURCE[0]}'."
    exit 1
fi

# Set THIS_SCRIPT_* vars and '/proc/pid/comm' to identify this script.
gpudb_set_proc_comm "gpudb-mq"
# Set the THIS_SCRIPT_* vars.
gpudb_set_this_script_info "${BASH_SOURCE[0]}" "$@"

GPUDB_HA_DIR=$(readlink -f $SCRIPT_DIR/../)
GPUDB_HA_LOG_DIR="$GPUDB_HA_DIR/logs"
GPUDB_MQ_SERVER_DIR="$GPUDB_HA_DIR/rabbitmq-server"

GPUDB_ROOT_DIR=$(readlink -m $GPUDB_HA_DIR/..)

RABBITMQ_SERVER_EXE="$GPUDB_MQ_SERVER_DIR/sbin/rabbitmq-server"

export GPUDB_PROCESS_ID="gpudb-mq:$(hostname):$GPUDB_HA_DIR"

# ---------------------------------------------------------------------------
# Configuration parameters

GPUDB_HA_USER="gpudb"

# ---------------------------------------------------------------------------

function mq_start
{
    local GPUDB_MQ_LOG_FILENAME="$GPUDB_HA_LOG_DIR/gpudb-MQ-${DATETIME}.log"
    local GPUDB_MQ_LOG_LINK_FILENAME="$GPUDB_HA_LOG_DIR/gpudb-MQ.log"

    if [[ "a$(mq_pid)" != "a" ]]; then
        echo "MQ already started, not restarting."
        return
    fi

    # make sure the .erlang.cookie is in the right location
    ln -s ${GPUDB_MQ_SERVER_DIR}/.erlang.cookie ~/ > /dev/null 2>&1

    # Must be in this dir to run
    pushd "$GPUDB_HA_DIR" > /dev/null

    echo -n "Starting MQ" | tee -a "${GPUDB_MQ_LOG_FILENAME}"
    echo "----------------" >> "$GPUDB_MQ_LOG_FILENAME"

    # Start rabbitmq-server
    run_cmd "nohup $RABBITMQ_SERVER_EXE start >> "${GPUDB_MQ_LOG_FILENAME}" 2>&1 &"

    local RETRIES=300
    while ! lsof -i :15672 > /dev/null; do
        sleep 2
        echo -n '.'
        local MQ_PID="$(mq_pid)"
        if [ -z "$MQ_PID" ]; then
            echo -e "\nERROR: Rabbitmq-server did not start properly, please see logs." | tee -a "${GPUDB_MQ_LOG_FILENAME}"
            exit 1
        fi
        RETRIES=$(($RETRIES - 1))
        if [ $RETRIES -eq 0 ]; then
            echo -e "\nERROR: Timeout waiting for rabbitmq-server to start, please see logs." | tee -a "${GPUDB_MQ_LOG_FILENAME}"
            exit 1
        fi
    done
    echo
    # Set gpudb-MQ policy
    run_cmd "$GPUDB_MQ_SERVER_DIR/sbin/rabbitmqctl set_policy gpudb-ha \"^gpudb_\" '{\"ha-sync-batch-size\":200,\"queue-mode\":\"lazy\",\"ha-mode\":\"all\",\"ha-sync-mode\":\"automatic\",\"queue-version\":2}' --apply-to all > /dev/null"

    ln -f -s "${GPUDB_MQ_LOG_FILENAME}" "${GPUDB_MQ_LOG_LINK_FILENAME}"
}

function run_cmd
{
    PATH="$GPUDB_HA_DIR/erlang/bin:$PATH" eval "$@"
}

function mq_pid
{
    local PS_GREP="$RABBITMQ_SERVER_EXE start"
    PS_GREP+="|${GPUDB_HA_DIR}.*/beam.smp .*"
    PS_GREP+="|${GPUDB_HA_DIR}.*/epmd "
    local ENV_GREP="RABBITMQ_CONF_ENV_FILE=$RABBITMQ_CONF_ENV_FILE"
    local PIDS=$(get_pids_for_app "$PS_GREP" "$ENV_GREP" || true)

    echo $PIDS
}

function mq_stop
{
    local MQ_PID="$(mq_pid)"

    if [[ "a$MQ_PID" == "a" ]]; then
        echo "MQ not running."
        return
    fi

    echo -n "Stopping MQ, pid: $MQ_PID"

    # Try running stop first.  Then get more serious.
    run_cmd "$GPUDB_MQ_SERVER_DIR/sbin/rabbitmqctl shutdown > /dev/null"
    # Just kill epmd, their -stop and -kill args need -relaxed_command_check arg at startup to force it.

    local MQ_PID="$(mq_pid)"

    if [[ "a$MQ_PID" != "a" ]]; then
        kill $MQ_PID
    fi

    # Wait for EXIT_SLEEP_TIME_SECS*EXIT_SLEEP_COUNT secs showing that we're still waiting...
    local EXIT_SLEEP_TIME_SECS=1
    local EXIT_SLEEP_COUNT=120

    local counter=0
    while [ $counter -lt $EXIT_SLEEP_COUNT ]; do
        if [[ "a$(mq_pid)" == "a" ]]; then
            echo ""
            return 0
        fi

        if [ $(($counter % 5)) -eq 0 ]; then
            echo -n "."
        fi
        sleep $EXIT_SLEEP_TIME_SECS
        counter=$((counter+1))
    done

    echo ""

    MQ_PID=$(mq_pid)
    if [[ "a$MQ_PID" == "a" ]]; then
        echo "MQ is stopped."
    else
        echo "Killing MQ, pid: $MQ_PID"
        kill -9 $MQ_PID
    fi

    return 0
}

function mq_status
{
    local MQ_PID=$(mq_pid)
    local NUM_PIDS=$(echo $MQ_PID | wc --words)

    if [ "$NUM_PIDS" -ge 2 ]; then
        echo "MQ is running, PID: $MQ_PID"
        return 0
    elif [ "$NUM_PIDS" -eq 0 ]; then
        echo "MQ is not running."
        return 1
    else
        echo "MQ is not fully running, please stop or restart."
        return 1
    fi
}

function mq_init
{
    # ---------------------------------------------------------------------------
    # Call this function once to check GPUDB_HA_USER and re-run this script as that user if necessary.
    # User needs to be correct when creating files and starting programs.
    # This script should normally only be called by 'root' or GPUDB_HA_USER, but does allow the owner
    # of the GPUDB_MQ_SERVER_DIR dir to run it for custom installs.

    # After this call we may restart this script from the beginning and exit.
    if ! switch_user_to "$GPUDB_HA_USER" "$GPUDB_MQ_SERVER_DIR" ; then
        echo "Error : Unable to run as user: '$GPUDB_HA_USER'."
        exit 1
    fi

    # Start in the root directory as a known starting point
    if ! pushd "$GPUDB_HA_DIR" > /dev/null; then
        echo "Error: Unable to change into '$GPUDB_HA_DIR' as user: '$(whoami)'."
        exit 1
    fi

    \mkdir -p "$GPUDB_HA_LOG_DIR" || exit 1
}

# ---------------------------------------------------------------------------

USAGE_STR="
This internal use program starts and stops the GPUdb MQ instance.
Prefer using 'systemctl start/stop gpudb-mq' to control the mq.
Before using this script's start/stop commands be sure to run 
'systemctl stop gpudb-mq && systemctl disable gpudb-mq' so they don't compete.

Commands:
 start                 : Start the mq service if not already running.
 restart               : Stop then start the mq service.
 stop                  : Stop the mq service if running.
 status                : Show status info about the mq, returns 0 if running.

 rabbitmqctl ARG1 ...  : Run rabbitmqctl [...] as the '$GPUDB_HA_USER' user with the
                         environment set correctly. Try 'help', 'status', or
                         rabbitmqctl eval \"'application:get_all_env(rabbit).'\"
 run-cmd CMD ARG1 ...  : Run a command as the '$GPUDB_HA_USER' user with the
                         environment set correctly.

Usage: $0 {start|stop|status|restart}
"
# ---------------------------------------------------------------------------

case "$1" in

    'start')
        mq_init && mq_start
        exit $?
        ;;

    'stop')
        mq_init && mq_stop
        exit $?
        ;;

    'restart')
        mq_init && mq_stop && mq_start && mq_status
        exit $?
        ;;

    'status')
        mq_init && mq_status && run_cmd "$GPUDB_MQ_SERVER_DIR/sbin/rabbitmqctl" -t 30 status
        exit $?
        ;;

    'run-cmd'|'run_cmd')
        shift
        mq_init && run_cmd "$@"
        exit $?
        ;;

    'rabbitmqctl')
        shift
        mq_init && run_cmd "$GPUDB_MQ_SERVER_DIR/sbin/rabbitmqctl" "$@"
        exit $?
        ;;

    'help'|'-h'|'--help')
        echo "$USAGE_STR"
        exit 0
        ;;

    *)
        echo "Unknown arg: '$1' in '$0 $*', exiting."
        echo
        echo "$USAGE_STR"
        exit 1
        ;;

esac
