#!/bin/bash -e

if [[ "$EUID" != "0" ]]; then
    echo "This script must be run as root"
    exit 2
fi

# A workaround for running this script from the devel environment

if [[ -d cumin && -f etc/devel.profile ]]; then
    source etc/devel.profile &> /dev/null
fi

pgdata="/var/lib/pgsql/data"
pglog="${pgdata}/pg_log"
pghbaconf="${pgdata}/pg_hba.conf"
dbname="cumin"

function format-output {
    while read line; do
        echo " | $line"
    done
}

function run {
    echo " | \$ $1"

    if [[ "$2" ]]; then
        su - postgres -c "$1" | format-output 2>&1
    else
        $1 | format-output 2>&1
    fi

    return ${PIPESTATUS[0]}
}

function retry {
    completed=1
    for ((c=0; c<=$2; c++))
    do
        $1 && completed=0
        if [ $completed -eq 0 ]; then
            break
        fi
        sleep 1
    done
    return $completed
}

function check-environment {
    which rpm > /dev/null

    rpm -q postgresql-server &> /dev/null || {
        echo "Error: postgresql-server is not installed"
        if [ "$1" != noadvice ]; then
            echo "Hint: Run 'yum install postgresql-server'"
        fi
        return 1
    }
}

function check-started {
    /sbin/service postgresql status &> /dev/null || {
        echo "Error: The database is not running"
        if [ "$1" != noadvice ]; then
            echo "Hint: Run 'cumin-database start'"
        fi
        return 1
    }
}

function check-initialized {
    if [[ ! -f "$pghbaconf" ]]; then
        echo "Error: The database is not initialized"
        if [ "$1" != noadvice ]; then
            echo "Hint: Run 'cumin-database install'"
        fi
        return 1
    fi
}

function check-configured {
    grep "$dbname" "$pghbaconf" &> /dev/null || {
        echo "Error: The database is not configured"
        if [ "$1" != noadvice ]; then
            echo "Hint: Run 'cumin-database install'"
        fi
        return 1
    }
}

function check-created {
    psql -d cumin -U cumin -h localhost -c '\q' &> /dev/null || {
        echo "Error: The database is not created"
        if [ "$1" != noadvice ]; then
            echo "Hint: Run 'cumin-database install'"
        fi
        return 1
    }
}

function check-created-wait {
    for ((c=0; c<=30; c++))
    do
        res="$(psql -d cumin -U cumin -h localhost -c '\q' 2>&1)"
        case $res in
            *"could not connect"*)
                if [ $c -eq 0 ] ; then
                    echo
                fi
                echo could not connect to server, retry...
                sleep 1 
                ;;
            "")  
                return 0
                ;;
            *) 
                echo "Error: The database is not created"
                if [ "$1" != noadvice ]; then
                    echo "Hint: Run 'cumin-database install'"
                fi
                return 1
                ;;
        esac
    done
    echo "Error: Could not connect to server"
    return 1
}

function confirm-install {
    cat <<EOF
WARNING

This script installs a cumin database into the system postgresql
instance.  It will take the following actions as necessary.

 * It will stop and start the postgresql service.

 * It will initialize the postgresql database cluster if it isn't
   already initialized.

 * It will alter postgresql configuration files.

If you already have a custom-configured postgresql install, you may
not want to proceed.

If there are clients that depend on the running postgresql service,
you probably don't want to proceed.

If, however, none of these changes affect your existing deployment, it
is safe to proceed.

EOF

    get-explicit-confirmation

    install
}

function get-explicit-confirmation {
    echo "Enter 'yes' to proceed or Ctrl-C to cancel:"

    while read word; do
        if [[ "$word" == "yes" ]]; then
            break
        fi

        echo "Enter 'yes' to proceed or Ctrl-C to cancel:"
    done
}

function install {
    check-environment || exit 1

    check-initialized &> /dev/null || initialize

    check-configured &> /dev/null || {
        configure
        restart > /dev/null
    }

    check-started &> /dev/null || start > /dev/null

    check-created-wait &> /dev/null || create
}

function start {
    check-environment || exit 1
    check-initialized || exit 1

    /sbin/service postgresql start
}

function stop {
    check-environment || exit 1
    check-initialized || exit 1

    /sbin/service postgresql stop
}

function restart {
    check-environment || exit 1
    check-initialized || exit 1

    # The postgresql 8.1.22 init script violates LSB 3.1 Chapter 20.2,
    # and returns 1 on a successful restart of a stopped service.
    # Use condrestart to skip restart of stopped service.

    # Apparently condstop sometimes fails.  Try up to 5 times.
    retry "/sbin/service postgresql condrestart" 5 || {
        echo "postgres restart failed"
        exit 1
    }
}

function initialize {
    check-environment || exit 1
    
    if check-initialized &> /dev/null; then
        echo "Error: The database server is already initialized"
        exit 1
    fi

    if check-started &> /dev/null; then
        echo "Error: The database server is running"
        echo "Hint: Run 'cumin-database stop'"
        exit 1
    fi

    # On older versions of Postgres (before 8.4), initdb does not exist but
    # using the start option will initialize the database, then start it.
    # Try initdb first, and if it fails try start.  The "start" method
    # will leave the service running, which is different than initdb,
    # but that is fine at this point because it is restarted after
    # the call to configure anyway.
    echo -e "\nAttempting to use initdb option..."
    run "/sbin/service postgresql initdb" || {
        echo -e "\ninitdb option not supported, using start..."
        run "/sbin/service postgresql start"
    }

    # now check again, to make sure one of the two methods worked
    check-initialized &> /dev/null || {
        echo "Error: Database initialization failed."
        exit 1
    }
}

function configure {
    check-environment || exit 1
    check-initialized || exit 1

    if check-configured &> /dev/null; then
        echo "Error: The database server is already configured"
        echo "(Note, the server must be restarted after configuration."
        echo " If it has not been restarted since configuration, use"
        echo " 'cumin-database stop' and 'cumin-database start' to restart it)"
        exit 1
    fi

    python <<EOF
import os
import sys

home = os.environ.get("CUMIN_HOME", os.path.normpath("/usr/share/cumin"))
sys.path.append(os.path.join(home, "python"))

from cumin.database import modify_pghba_conf

modify_pghba_conf('${pghbaconf}', '${dbname}', 'cumin')
EOF
}

function create {
    check-environment || exit 1
    check-started || exit 1
    check-configured || exit 1

    if check-created &> /dev/null; then
        echo "Error: The database is already created"
        exit 1
    fi

    run "createuser --superuser ${dbname}" postgres
    run "createdb --owner=${dbname} ${dbname}" postgres

    cumin-admin create-schema > /dev/null
    # cumin-admin add-role user
    # cumin-admin add-role admin
}

function drop {
    check-environment || exit 1
    check-started || exit 1

    run "dropdb ${dbname}" postgres
    run "dropuser ${dbname}" postgres
}

function confirm-drop {
    cat <<EOF
WARNING

This operation will discard the contents of the cumin database.

EOF

    get-explicit-confirmation

    drop
}

function confirm-annihilate {
    check-environment || exit 1

    echo "Really?"

    while read line; do
        if [[ "$line" == "really" ]]; then
            break
        fi

        echo "Not good enough"
    done

    run "/sbin/service postgresql stop" || :
    run "rm -rf /var/lib/pgsql/data"
}

function usage {
    cat <<EOF
Control and configure the cumin database
Usage: cumin-database COMMAND
Commands:
    check         Check the cumin database
    install       Automated database install
    start         Start the database server
    stop          Stop the database server
    initialize    Create the main database cluster
    configure     Configure the main database cluster
    create        Create the user, database, and schema
    drop          Discard the database user, database, and all data
EOF
    exit 1
}

case "$1" in
    check)
        echo -n "Checking environment ........ "
        check-environment $2 || exit 1
        echo "OK"

        echo -n "Checking initialization ..... "
        check-initialized $2 || exit 1
        echo "OK"

        echo -n "Checking configuration ...... "
        check-configured $2 || exit 1
        echo "OK"

        echo -n "Checking server ............. "
        check-started $2 || exit 1
        echo "OK"

        echo -n "Checking database 'cumin' ... "
        check-created-wait $2 || exit 1
        echo "OK"

        echo "The database is ready"
        ;;
    check-started)
        check-started || exit 1
        ;;
    install)
        confirm-install
        echo "The database is installed"
        ;;
    start)
        start
        echo "The database server is started"
        ;;
    stop)
        stop
        echo "The database server is stopped"
        ;;
    initialize)
        initialize
        echo "The database server is initialized"
        ;;
    configure)
        configure && {
            echo "The database server is configured"
            /sbin/service postgresql status &> /dev/null && {
                echo -e "\nAfter configuration the server must be restarted."
                echo -e "Would you like to restart the server now?\n"
                get-explicit-confirmation
                restart
            }
        }
        ;;
    create)
        create
        echo "The database is created"
        ;;
    drop)
        confirm-drop
        echo "The database is dropped"
        ;;
    annihilate)
        confirm-annihilate
        echo "You devastate me"
        ;;
    *)
        usage
        ;;
esac
