#! /bin/bash -e
#
# MAAS database control script.  See main() at the bottom for usage.
#
# Most functions take as their first argument a database cluster's data
# directory.  This is where the database's socket, pidfile, log, and data will
# reside.
#
# Some design choices for this module:
#
#  * Everything is PostgreSQL on Ubuntu.
#  * Each branch gets its own cluster.  Kill & delete when done.
#  * Databases run under the system user that creates them.  No root required.
#  * No global configuration apart from a basic PostgreSQL install.
#  * Connections use Unix sockets.  No TCP port hogging.

POSTGRES_VERSION=9.1
PGCTL="/usr/lib/postgresql/${POSTGRES_VERSION}/bin/pg_ctl"


# Figure out the full absolute data directory path for a given cluster, even
# if a relative path is given.
maasdb_locate() {
    local DATADIR
    DATADIR="$1"

    if test -z "$1"
    then
        echo "Specify a data directory for the MAAS database cluster." >&2
        return 1
    fi
    if ! echo "$DATADIR" | grep '^/'
    then
        echo "`pwd`/$DATADIR"
    fi
}


# Create a database cluster.
maasdb_create_cluster() {
    local DATADIR
    DATADIR="`maasdb_locate "$1"`"

    if ! test -d "$DATADIR/base"
    then
        mkdir -p -- "$DATADIR"
        $PGCTL init -s -D "$DATADIR" -o '-E utf8 -A trust'
    fi
}


# Start a database cluster.
maasdb_start_cluster() {
    local DATADIR DISPOSABLE EXTRA_POSTGRES_OPTS
    DATADIR="`maasdb_locate "$1"`"
    # Pass "disposable" as the second argument if the data in this database
    # is not important at all and you're willing to cut corners for speed.
    DISPOSABLE="$2"

    if test "$DISPOSABLE" = "disposable"
    then
        #  -F -- don't bother fsync'ing.
        EXTRA_POSTGRES_OPTS="-F"
    else
        EXTRA_POSTGRES_OPTS=""
    fi

    maasdb_create_cluster "$DATADIR"

    if ! test -f "$DATADIR/postmaster.pid"
    then
        # pg_ctl options:
        #  -D <dir> -- data directory.
        #  -l <file> -- log file.
        #  -s -- no informational messages.
        #  -w -- wait until startup is complete.
        # postgres options:
        #  -h <arg> -- host name; empty arg means Unix socket only.
        #  -k -- socket directory.
        $PGCTL start \
            -D "$DATADIR" -l "$DATADIR/backend.log" -s -w \
            -o "-h '' -k '$DATADIR' $EXTRA_POSTGRES_OPTS"
    fi
}


# Stop a database cluster.
maasdb_stop_cluster() {
    local DATADIR
    DATADIR="`maasdb_locate "$1"`"

    if test -f "$DATADIR/postmaster.pid"
    then
        $PGCTL stop -W -m fast -D "$DATADIR"
    fi
}


# Initialize a MAAS database.
maasdb_init_db() {
    local DATADIR DISPOSABLE MARKER
    DATADIR="`maasdb_locate "$1"`"
    # Pass "disposable" as the second argument if the data in this database
    # is not important at all and you're willing to cut corners for speed.
    DISPOSABLE="$2"

    maasdb_start_cluster "$DATADIR" "$DISPOSABLE"

    MARKER="$DATADIR/maas-created"
    if ! test -f "$MARKER"
    then
        createdb -h "$DATADIR" maas && touch "$MARKER"
    fi
}


# Open a psql shell on a MAAS database.
maasdb_shell() {
    local DATADIR
    DATADIR="`maasdb_locate "$1"`"

    maasdb_init_db "$DATADIR"
    psql -h "$DATADIR" maas
}


# Execute a query on a MAAS database.
maasdb_query() {
    local DATADIR QUERY
    DATADIR="`maasdb_locate "$1"`"
    QUERY="$2"

    maasdb_init_db "$DATADIR"
    psql -h "$DATADIR" maas -c "$QUERY"
}


# Delete an entire MAAS database and cluster.  Use only with extreme care!
maasdb_delete_cluster() {
    local DATADIR
    DATADIR="`maasdb_locate "$1"`"

    # Before deleting anything, does this at least look like a MAAS database
    # cluster?
    if test -d "$DATADIR/base"
    then
        maasdb_stop_cluster "$DATADIR"
        # Removing the data directory may fail because of a race condition
        # where db/global is nonempty because of a statistics write.  Rather
        # than block on shutdown, be optimistic and spin on retries.
        while ! rm -rf -- "$DATADIR"
        do
            # If this fails for a more persistent reason, too bad.  Ctrl-C.
            echo "Retrying deletion of $DATADIR." >&2
            sleep 0.01
        done
    fi
}


usage() {
    cat <<EOF >&2
Usage: maasdb <command> <cluster-path>

Where <command> is one of:
  start
  stop
  query
  shell
  delete-cluster

And <cluster-path> is the path to the MAAS development database cluster,
typically "./db"
EOF
}


unknown_command() {
    echo >&2 "** Unknown command: $1 **"
    echo
    usage
    exit 1
}


main() {
    local COMMAND DATADIR
    COMMAND="$1"
    DATADIR="$2"
    if ! shift 2
    then
        usage
        exit 1
    fi

    case "$COMMAND" in
        start) maasdb_init_db "$DATADIR" "$@" ;;
        stop) maasdb_stop_cluster "$DATADIR" "$@" ;;
        query) maasdb_query "$DATADIR" "$@" ;;
        shell) maasdb_shell "$DATADIR" "$@" ;;
        delete-cluster) maasdb_delete_cluster "$DATADIR" "$@" ;;
        *) unknown_command "$COMMAND" ;;
    esac
}


main "$@"
