#!/bin/sh

# Create a throw-away PostgreSQL environment for running regression tests. This
# happens on unshared tmpfses, so does not interfere with installed clusters.
#
# (C) 2005-2012 Martin Pitt <mpitt@debian.org>
# (C) 2012-2013 Christoph Berg <myon@debian.org>
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.

set -e

if [ "$(id -u)" != 0 ]; then
    echo "Error: pg_virtualenv needs to be run as root" >&2
    exit 1
fi

# call ourselves through unshare in a way that keeps normal stdin
if [ -z "$UNSHARED" ]; then
    UNSHARED=1 exec unshare -uimn -- "$0" "$@"
fi
# unshared program starts here

help ()
{
    echo "pg_virtualenv: Create throw-away PostgreSQL environment for regression tests"
    echo "Syntax: $0 [options] command"
    echo "    -a                use all installed server versions"
    echo "    -v 'version ...'  list of PostgreSQL versions to run [default: latest]"
    echo "    -c 'options'      extra options to pass to pg_createcluster"
    echo "    -i 'initdb opts'  extra initdb options to pass to pg_createcluster"
    echo "    -s                open a shell when command fails"
    exit ${1:-0}
}

# option parsing
PG_VERSIONS=""
while getopts "ac:i:hsv:" opt ; do
    case $opt in
	a) for d in /usr/lib/postgresql/*/bin/pg_ctl; do
		# prepend version so latest ends up first (i.e. on port 5432)
		PG_VERSIONS="$(basename ${d%%/bin/pg_ctl}) $PG_VERSIONS"
	   done ;;
	c) CREATE_OPTS="$OPTARG" ;;
	i) INITDB_OPTS="$OPTARG" ;;
	h) help ;;
	s) run_shell=1 ;;
	v) PG_VERSIONS="$OPTARG" ;;
	*) help 1 ;;
    esac
done
if [ -z "$PG_VERSIONS" ]; then
    # use latest version
    d=$(ls /usr/lib/postgresql/*/bin/pg_ctl | tail -1)
    PG_VERSIONS="$(basename ${d%%/bin/pg_ctl})"
fi
# shift away args
shift $(($OPTIND - 1))
[ "$1" ] || help 1

# let everything happen in overlay tmpfses to avoid interfering with already
# existing clusters; this also speeds up testing
created_dirs=""
for d in /etc/postgresql /var/lib/postgresql /var/log/postgresql /var/run/postgresql; do
    if ! [ -d $d ]; then
	created_dirs="$created_dirs $d"
	mkdir -p $d
    fi
    mount -n -t tmpfs -o mode=755 tmpfs $d
done
pg_service="/etc/postgresql-common/pg_service.conf"
# clean up created clusters and directories after us
cleanup () {
    set +e
    for v in $PG_VERSIONS; do
	echo "Stopping cluster $v/regress..."
	pg_ctlcluster $v regress stop --force
    done
    if [ "$created_dirs" ]; then
	umount $created_dirs
	rmdir --ignore-fail-on-non-empty -p $created_dirs
    fi
    rm -f $pg_service
    if [ -f $pg_service.pg_virtualenv-save ]; then
	mv -f $pg_service.pg_virtualenv-save $pg_service
    fi
}
trap cleanup 0 HUP INT QUIT ILL ABRT PIPE TERM
chown root:postgres /var/log/postgresql
chmod 1775 /var/log/postgresql
chown postgres:postgres /var/run/postgresql
chmod 2775 /var/run/postgresql
if [ -f $pg_service ]; then
    mv -f $pg_service $pg_service.pg_virtualenv-save
fi

# reset core limit for pg_ctl tests
ulimit -S -c 0

# start localhost interface
ifconfig lo up

# set variables which cause taint check errors
export IFS=' '
export CDPATH=/usr
export ENV=/nonexisting
export BASH_ENV=/nonexisting

# create postgres environments
if [ -x /usr/bin/pwgen ]; then
    export PGPASSWORD=$(pwgen 20 1)
else
    export PGPASSWORD=$(dd if=/dev/urandom bs=1k count=1 2>/dev/null | md5sum - | awk '{ print $1 }')
fi

for v in $PG_VERSIONS; do
    # create temporary cluster
    # we chdir to / so programs don't throw "could not change directory to ..."
    (
	cd /
	pg_createcluster ${CREATE_OPTS:-} --start $v regress -- ${INITDB_OPTS:-}
    )
    # set postgres password and retrieve port number
    port=$(cd / && su -c "psql --cluster $v/regress -qtA \
	-c \"ALTER USER postgres PASSWORD '$PGPASSWORD'; SHOW port\"" postgres)

    # record cluster information in service file
    cat >> /etc/postgresql-common/pg_service.conf <<EOF
[$v]
host=localhost
port=$port
dbname=postgres
user=postgres
password=$PGPASSWORD

EOF
done

export PGHOST="localhost"
export PGDATABASE="postgres"
export PGUSER="postgres"
# we do not set PGPORT here because that breaks --cluster
# (the first cluster created will use port 5432 anyway)

# run program
if [ "$run_shell" ]; then
    "$@" || {
	echo "pg_virtualenv: command exited with status $?, dropping you into a shell"
	$SHELL
    }
else
    "$@"
fi
