#!/bin/bash # Create a throw-away PostgreSQL environment for running regression tests. # This does not interfere with existing clusters. # # (C) 2005-2012 Martin Pitt # (C) 2012-2020 Christoph Berg # # 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 # no -u here as that breaks PGCONF_OPTS[@] # wrap ourselves in newpid if requested if [ "$PG_VIRTUALENV_NEWPID" ]; then unset PG_VIRTUALENV_NEWPID exec newpid $0 "$@" fi # wrap ourselves in unshare if requested if [ "$PG_VIRTUALENV_UNSHARE" ]; then export _PG_VIRTUALENV_UNSHARE="$PG_VIRTUALENV_UNSHARE" unset PG_VIRTUALENV_UNSHARE exec unshare $_PG_VIRTUALENV_UNSHARE -- $0 "$@" fi if [ "$_PG_VIRTUALENV_UNSHARE" ]; then unset _PG_VIRTUALENV_UNSHARE # start localhost interface if [ -x /bin/ip ]; then ip link set dev lo up || true else ifconfig lo up || true fi fi disable_fakeroot () { case ${LD_PRELOAD:-} in *fakeroot*) LD_PRELOAD=$(echo "$LD_PRELOAD" | sed -e 's/[^ ]*fakeroot[^ ]*//g') ;; esac } 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 " -o 'guc=value' postgresql.conf options to pass to pg_createcluster" echo " -p 'package' set options to find extension files in debian/package/" echo " -s open a shell when command fails" echo " -t use a temporary cluster directory even as root" exit ${1:-0} } # option parsing PGBINROOT="/usr/lib/postgresql/" #redhat# PGBINROOT="/usr/pgsql-" PG_VERSIONS="" PGCONF_OPTS=() while getopts "ac:i:ho:p:stv:" opt ; do case $opt in a) for d in $PGBINROOT*/bin/pg_ctl; do # prepend version so latest ends up first (i.e. on port 5432) dir=${d%%/bin/pg_ctl} PG_VERSIONS="${dir#$PGBINROOT} ${PG_VERSIONS:-}" done ;; c) CREATE_OPTS="$OPTARG" ;; i) INITDB_OPTS="$OPTARG" ;; h) help ;; o) PGCONF_OPTS+=("--pgoption" "$OPTARG") ;; p) PACKAGE="$OPTARG" ;; s) run_shell=1 ;; t) NONROOT=1 ;; v) PG_VERSIONS="$OPTARG" ;; *) help 1 ;; esac done if [ -z "$PG_VERSIONS" ]; then # use latest version d=$(ls -v $PGBINROOT*/bin/pg_ctl 2> /dev/null | tail -1) if [ -z "$d" ]; then echo "Could not determine PostgreSQL version, are any PostgreSQL server packages installed?" >&2 exit 2 fi dir=${d%%/bin/pg_ctl} PG_VERSIONS="${dir#$PGBINROOT}" fi # shift away args shift $(($OPTIND - 1)) # if no command is given, open a shell [ "${1:-}" ] || set -- ${SHELL:-/bin/sh} # generate a password 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 # we are not root if [ "$(id -u)" != 0 ]; then NONROOT=1 fi # we aren't really root in fakeroot (but leave it enabled for commands spawned) case ${LD_PRELOAD:-} in *fakeroot*) NONROOT=1 ;; esac # non-root operation: create a temp dir where we store everything if [ "${NONROOT:-}" ]; then WORKDIR=$(mktemp -d -t pg_virtualenv.XXXXXX) if [ $(id -u) = 0 ]; then chown postgres:postgres $WORKDIR umask 022 fi export PG_CLUSTER_CONF_ROOT="$WORKDIR/postgresql" export PGUSER="${USER:-${LOGNAME:-$(id -un)}}" [ "$PGUSER" = "root" ] && PGUSER="postgres" PGSYSCONFDIR="$WORKDIR/postgresql-common" # no export yet so pg_createcluster uses the original createcluster.conf mkdir "$PGSYSCONFDIR" "$WORKDIR/log" PWFILE="$PGSYSCONFDIR/pwfile" LOGDIR="$WORKDIR/log" echo "$PGPASSWORD" > "$PWFILE" cleanup () { set +e disable_fakeroot for v in $PG_VERSIONS; do # don't drop existing clusters named "regress" [ -f $PG_CLUSTER_CONF_ROOT/$v/regress/.by_pg_virtualenv ] || continue echo "Dropping cluster $v/regress ..." pg_ctlcluster --mode immediate $v regress stop pg_dropcluster $v regress done rm -rf $WORKDIR } trap cleanup 0 HUP INT QUIT ILL ABRT PIPE TERM # root: keep everything in the standard locations else for v in $PG_VERSIONS; do if [ -d /etc/postgresql/$v/regress ]; then echo "Cluster $v/regress exists, refusing to overwrite" >&2 exit 2 fi done : ${PGSYSCONFDIR:=/etc/postgresql-common} pg_service="$PGSYSCONFDIR/pg_service.conf" export PGUSER="postgres" PWFILE=$(mktemp -t pgpassword.XXXXXX) echo "$PGPASSWORD" > "$PWFILE" # write password before chowning the file chown postgres:postgres "$PWFILE" cleanup () { set +e rm -f $PWFILE $pg_service if [ -f $pg_service.pg_virtualenv-save.$$ ]; then mv -f $pg_service.pg_virtualenv-save.$$ $pg_service fi for v in $PG_VERSIONS; do # don't drop existing clusters named "regress" [ -f /etc/postgresql/$v/regress/.by_pg_virtualenv ] || continue echo "Dropping cluster $v/regress ..." rm -f /etc/postgresql/$v/regress/.by_pg_virtualenv pg_ctlcluster --mode immediate $v regress stop pg_dropcluster $v regress done } trap cleanup 0 HUP INT QUIT ILL ABRT PIPE TERM if [ -f $pg_service ]; then mv --no-clobber $pg_service $pg_service.pg_virtualenv-save.$$ fi fi # create postgres environments for v in $PG_VERSIONS; do # create temporary cluster if [ "${PACKAGE:-}" ]; then if ! grep -q 'extension_destdir' /usr/share/postgresql/$v/postgresql.conf.sample; then echo "$0: This PostgreSQL $v installation does not support 'extension_destdir', skipping this version" [ "$PG_VERSIONS" = "$v" ] && exit 0 # only one version requested continue fi PKGARGS="--pgoption extension_destdir=$PWD/debian/$PACKAGE --pgoption dynamic_library_path=$PWD/debian/$PACKAGE/usr/lib/postgresql/$v/lib:/usr/lib/postgresql/$v/lib" fi # we chdir to / so programs don't throw "could not change directory to ..." ( cd / case $v in 8*|9.0|9.1|9.2) : ;; *) NOSYNC="--nosync" ;; esac if [ "${NONROOT:-}" ]; then # stats_temp_directory in /var/run/postgresql needs root (or postgres), disable it case $v in 8.4|9.*|1[01234]) PGCONF_OPTS+=("--pgoption" "stats_temp_directory=") ;; esac fi # disable fakeroot here because we really can't run as root # (and still switch to postgres when using fakeroot as root user) disable_fakeroot pg_createcluster --quiet \ ${PGPORT:+-p "$PGPORT"} \ ${NONROOT:+-d "$WORKDIR/data/$v/regress"} \ ${NONROOT:+-l "$WORKDIR/log/postgresql-$v-regress.log"} \ ${CREATE_OPTS:-} --pgoption fsync=off ${PKGARGS:-} "${PGCONF_OPTS[@]}" \ $v regress -- \ --username="$PGUSER" --pwfile="$PWFILE" $NOSYNC ${INITDB_OPTS:-} # in fakeroot, the username will likely default to "postgres" otherwise echo "This is a temporary throw-away cluster" > ${PG_CLUSTER_CONF_ROOT:-/etc/postgresql}/$v/regress/.by_pg_virtualenv # start cluster with coredumps enabled [ $v != 8.2 ] && ENABLE_COREDUMPS="-c" pg_ctlcluster $v regress start -- ${ENABLE_COREDUMPS:-} ) port=$(pg_conftool -s $v regress show port) # record cluster information in service file cat >> $PGSYSCONFDIR/pg_service.conf < /dev/null; then for v in $PG_VERSIONS; do PGDATA=$(pg_conftool -s $v regress show data_directory) for core in $PGDATA/core*; do [ -f "$core" ] || continue echo "*** $core ***" gdb -batch -ex "bt full" /usr/lib/postgresql/$v/bin/postgres "$core" done done fi if [ "${run_shell:-}" ]; then echo "pg_virtualenv: command exited with status $EXIT, dropping you into a shell" ${SHELL:-/bin/sh} fi fi exit ${EXIT:-0}