#!/bin/bash
#
# postgresql-setup - Initialization and upgrade operations for PostgreSQL

test x"${PGSETUP_DEBUG}" != x && set -x

# PGVERSION is the full package version, e.g., 9.0.2
# Note: the debian/rules file substitutes the correct value during package build
PGVERSION=9.4.7

# PGMAJORVERSION is the major version, e.g. 9.0
# Note: the debian/rules file substitutes the correct value during package build
PGMAJORVERSION=9.4

# PGENGINE is the directory containing the postmaster executable
PGENGINE=/opt/puppetlabs/server/apps/postgresql/bin

# PREVMAJORVERSION is the previous major version, e.g., 8.4, for upgrades
# Note: the debian/rules file substitutes the correct value during package build
PREVMAJORVERSION=9.2

# PREVPGENGINE is the directory containing the previous postmaster executable
PREVPGENGINE=/opt/puppetlabs/server/apps/postgresql/lib/pgsql/postgresql-9.2/bin

# User and group to chown database to
PGUSER="pe-postgres"
PGGROUP="pe-postgres"

# Absorb configuration settings from the specified systemd service file,
# or the default "postgresql" service if not specified
SERVICE_NAME="$2"
if [ x"${SERVICE_NAME}" = x ]; then
    SERVICE_NAME=pe-postgresql
fi

# Pathname of the distribution README
README_DIST="/opt/puppetlabs/server/share/doc/pe-postgresql-$PGVERSION/README"

USAGE_STRING=$"
Usage: $0 {initdb|upgrade} [SERVICE_NAME]

Script is aimed to help sysadmin with basic database cluster administration.

The SERVICE_NAME is used for selection of proper unit configuration file; For
more info and howto/when use this script please look at the docu file
${README_DIST}.  The 'postgresql'
string is used when no SERVICE_NAME is explicitly passed.

Available operation mode:
  initdb        Create a new PostgreSQL database cluster.  This is usually the
                first action you perform after PostgreSQL server installation.
  upgrade       Upgrade PostgreSQL database cluster to be usable with new
                server.  Use this if you upgraded your PostgreSQL server to
                newer major version (currently from ${PREVMAJORVERSION} \
to ${PGMAJORVERSION}).

Environment:
  PGSETUP_INITDB_OPTIONS     Options carried by this variable are passed to
                             subsequent call of \`initdb\` binary (see man
                             initdb(1)).  This variable is used also during
                             'upgrade' mode because the new cluster is actually
                             re-initialized from the old one.
  PGSETUP_PGUPGRADE_OPTIONS  Options in this variable are passed next to the
                             subsequent call of \`pg_upgrade\`.  For more info
                             about possible options please look at man
                             pg_upgrade(1).
  PGSETUP_DEBUG              Set to '1' if you want to see debugging output."

# note that these options are useful at least for help2man processing
case "$1" in
    --version)
        echo "postgresql-setup ${PGVERSION}"
        exit 0
        ;;
    --help|--usage)
        echo "${USAGE_STRING}"
        exit 0
        ;;
esac

# The port PostgreSQL is running on
PGPORT=5432

# The path to where PostgreSQL databases are stored
PGDATA=/opt/puppetlabs/server/data/postgresql/${PGMAJORVERSION}/data

# Log file for initdb
PGLOG=/opt/puppetlabs/server/data/postgresql/initdb.log

# Log file for pg_upgrade
PGUPLOG=/opt/puppetlabs/server/data/postgresql/pgupgrade.log

export PGDATA
export PGPORT

# For SELinux we need to use 'runuser' not 'su'
if [ -x /sbin/runuser ]; then
    SU=runuser
else
    SU=su
fi

script_result=0

# code shared between initdb and upgrade actions
perform_initdb(){
    if [ ! -e "${PGDATA}" ]; then
        mkdir "${PGDATA}" || return 1
        chown "${PGUSER}:${PGGROUP}" "${PGDATA}"
        chmod go-rwx "${PGDATA}"
    fi
    # Clean up SELinux tagging for PGDATA
    [ -x /sbin/restorecon ] && /sbin/restorecon "${PGDATA}"

    # Create the initdb log file if needed
    if [ ! -e "${PGLOG}" -a ! -h "${PGLOG}" ]; then
        touch "${PGLOG}" || return 1
        chown "${PGUSER}:${PGGROUP}" "${PGLOG}"
        chmod go-rwx "${PGLOG}"
        [ -x /sbin/restorecon ] && /sbin/restorecon "${PGLOG}"
    fi

    # Initialize the database
    initdbcmd="${PGENGINE}/initdb --pgdata='${PGDATA}' --auth-local='peer' --auth-host='md5'"
    initdbcmd+=" ${PGSETUP_INITDB_OPTIONS}"

    ${SU} -l "${PGUSER}" -s /bin/bash -c "${initdbcmd}" >> "${PGLOG}" 2>&1 < /dev/null

    # Create directory for postmaster log files
    mkdir "${PGDATA}/pg_log"
    chown "${PGUSER}:${PGGROUP}" "${PGDATA}/pg_log"
    chmod go-rwx "${PGDATA}/pg_log"
    [ -x /sbin/restorecon ] && /sbin/restorecon "${PGDATA}/pg_log"

    if [ -f "${PGDATA}/PG_VERSION" ]; then
        return 0
    fi
    return 1
}

initdb(){
    if [ -f "${PGDATA}/PG_VERSION" ]; then
        echo $"Data directory is not empty!"
        echo
        script_result=1
    else
        echo -n $"Initializing database ... "
        if perform_initdb; then
            echo $"OK"
        else
            echo $"failed, see ${PGLOG}"
            script_result=1
        fi
        echo
    fi
}

upgrade(){
    # must see previous version in PG_VERSION
    if [ ! -f "${PGDATA}/PG_VERSION" -o \
         x`cat "${PGDATA}/PG_VERSION"` != x"${PREVMAJORVERSION}" ]
    then
        echo
        echo $"Cannot upgrade because the database in ${PGDATA} is not of"
        echo $"compatible previous version ${PREVMAJORVERSION}."
        echo
        exit 1
    fi

    # Set up log file for pg_upgrade
    rm -f "${PGUPLOG}"
    touch "${PGUPLOG}" || exit 1
    chown "${PGUSER}:${PGGROUP}" "${PGUPLOG}"
    chmod go-rwx "${PGUPLOG}"
    [ -x /sbin/restorecon ] && /sbin/restorecon "${PGUPLOG}"

    # Move old DB to PGDATAOLD
    PGDATAOLD="${PGDATA}-old"
    rm -rf "${PGDATAOLD}"
    mv "${PGDATA}" "${PGDATAOLD}" || exit 1

    # Create configuration file for upgrade process
    HBA_CONF_BACKUP="${PGDATAOLD}/pg_hba.conf.postgresql-setup.`date +%s`"
    HBA_CONF_BACKUP_EXISTS=0

    if [ ! -f ${HBA_CONF_BACKUP} ]; then
        mv "${PGDATAOLD}/pg_hba.conf" "${HBA_CONF_BACKUP}"
        HBA_CONF_BACKUP_EXISTS=1

        # For fluent upgrade 'postgres' user should be able to connect
        # to any database without password.  Temporarily, no other type
        # of connection is needed.
        echo "local all postgres ident" > "${PGDATAOLD}/pg_hba.conf"
    fi

    echo -n $"Upgrading database: "

    # Create empty new-format database
    if perform_initdb; then
        # Do the upgrade
        ${SU} -l "${PGUSER}"  -s /bin/bash -c "${PGENGINE}/pg_upgrade \
                        '--old-bindir=${PREVPGENGINE}' \
                        '--new-bindir=${PGENGINE}' \
                        '--old-datadir=${PGDATAOLD}' \
                        '--new-datadir=${PGDATA}' \
                        --link \
                        '--old-port=${PGPORT}' '--new-port=${PGPORT}' \
                        --user='pe-postgres' \
                        ${PGSETUP_PGUPGRADE_OPTIONS}" \
                                >> "${PGUPLOG}" 2>&1 < /dev/null
        if [ $? -ne 0 ]; then
            # pg_upgrade failed
            script_result=1
        fi
    else
        # initdb failed
        script_result=1
    fi

    # Move back the backed-up pg_hba.conf regardless of the script_result.
    if [ x${HBA_CONF_BACKUP_EXISTS} = x1 ]; then
        mv -f "${HBA_CONF_BACKUP}" "${PGDATAOLD}/pg_hba.conf"
    fi

    if [ ${script_result} -eq 0 ]; then
        echo $"OK"
        echo
        echo $"The configuration files were replaced by default configuration."
        echo $"The previous configuration and data are stored in folder"
        echo ${PGDATAOLD}.
    else
        # Clean up after failure
        rm -rf "${PGDATA}"
        mv "${PGDATAOLD}" "${PGDATA}"
        echo $"failed"
    fi
    echo
    echo $"See ${PGUPLOG} for details."
}

# See how we were called.
case "$1" in
    initdb)
        initdb
        ;;
    upgrade)
        upgrade
        ;;
    *)
        echo >&2 "${USAGE_STRING}"
        exit 2
esac

exit ${script_result}
