You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DevOps-Bash-tools/postgres.sh

213 lines
6.8 KiB
Bash

#!/usr/bin/env bash
# vim:ts=4:sts=4:sw=4:et
#
# Author: Hari Sekhon
# Date: 2020-08-05 13:42:41 +0100 (Wed, 05 Aug 2020)
#
# https://github.com/harisekhon/bash-tools
#
# License: see accompanying Hari Sekhon LICENSE file
#
# If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish
#
# https://www.linkedin.com/in/harisekhon
#
# https://hub.docker.com/_/postgres
set -euo pipefail
[ -n "${DEBUG:-}" ] && set -x
srcdir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1090
. "$srcdir/lib/utils.sh"
# shellcheck disable=SC1090
. "$srcdir/lib/dbshell.sh"
# defined in lib/dbshell.sh
# shellcheck disable=SC2154
shell_description="$sql_mount_description
Source a sql script:
\\i postgres_info.sql
Get shell access:
\\!
List available SQL scripts:
\\! ls -l postgres*.sql
"
# shellcheck disable=SC2034,SC2154
usage_description="
Boots a quick PostgreSQL docker container and drops you in to the 'psql' shell
Multiple invocations of this script will connect to the same Postgres container if already running
and the last invocation of this script to exit from the psql shell will delete that container
PostgresSQL version can be specified using the first argument, or the \$POSTGRES_VERSION environment variable,
otherwise 'latest' is used
Versions to use can be found from the following URL:
https://hub.docker.com/_/postgres?tab=tags
or programmatically on the command line (see DevOps Python tools repo):
dockerhub_show_tags.py postgres
Automatically creates shared bind mount points from host to container for convenience:
$shell_description
"
# used by usage() in lib/utils.sh
# shellcheck disable=SC2034
usage_args="[<version>] [options]
-n --name NAME Docker container name to use (default: postgres)
-p --port PORT Expose PostgreSQL port 5432 on given port number
-d --no-delete Don't delete the container upon the last psql session closing (\$DOCKER_NO_DELETE)
-r --restart Force a restart of a clean PostgreSQL instance (\$POSTGRES_RESTART)
-s --sample Load sample Chinook database (\$LOAD_SAMPLE)"
help_usage "$@"
docker_image=postgres
port=""
docker_opts=""
password="${PGPASSWORD:-${POSTGRESQL_PASSWORD:-${POSTGRES_PASSWORD:-${PASSWORD:-test}}}}"
while [ $# -gt 0 ]; do
# DOCKER_NO_DELETE used by functions from lib
# shellcheck disable=SC2034
case "$1" in
-n| --name) container_name="$2"
shift
;;
-p| --port) port="$2"
[[ "$port" =~ ^[[:digit:]]*$ ]] || die "invalid --port '$port' given"
shift
;;
-s|--sample) LOAD_SAMPLE_DB=1
;;
-r|--restart) POSTGRES_RESTART=1
;;
-d|--no-delete) DOCKER_NO_DELETE=1
;;
*) version="$1"
;;
esac
shift
done
container_name="${container_name:-${POSTGRES_CONTAINER_NAME:-postgres}}"
version="${version:-${POSTGRESQL_VERSION:-${POSTGRES_VERSION:-latest}}}"
if [ -n "$port" ]; then
docker_opts="-p $port:5432"
fi
db="$srcdir/chinook.psql"
if [ -n "${LOAD_SAMPLE_DB:-}" ] &&
! [ -f "$db.utf8" ]; then
timestamp "downloading sample 'chinook' database"
wget -qcO "$db" 'https://github.com/lerocha/chinook-database/blob/master/ChinookDatabase/DataSources/Chinook_PostgreSql.sql?raw=true'
iconv -f ISO-8859-1 -t UTF-8 "$db" > "$db.utf8"
fi
# kill existing if we have specified a different version than is running
docker_image_version="$(docker_container_image "$container_name")"
if [ -n "$docker_image_version" ] &&
[ "$docker_image_version" != "$docker_image:$version" ]; then
POSTGRES_RESTART=1
fi
# remove existing non-running container so we can boot a new one
if docker_container_not_running "$container_name"; then
POSTGRES_RESTART=1
fi
if [ -n "${POSTGRES_RESTART:-}" ]; then
# ensures version is correct before we kill any existing test env to switch versions to minimize downtime
timestamp "docker pull $docker_image:$version"
docker_pull "$docker_image:$version"
timestamp "killing existing container:"
docker rm -f "$container_name" 2>/dev/null || :
fi
if ! docker_container_exists "$container_name"; then
timestamp "booting PostgreSQL container from image '$docker_image:$version':"
# defined in lib/dbshell.sh
# shellcheck disable=SC2154,SC2086
docker run -d \
--name "$container_name" \
$docker_opts \
-e POSTGRES_PASSWORD="$password" \
-v "$srcdir/setup/postgresql.conf:/etc/postgresql/postgresql.conf" \
$docker_sql_mount_switches \
"$docker_image":"$version" \
"$(if [ "${version:0:1}" = 8 ] || [ "${version:0:3}" = '9.0' ]; then echo postgres; fi)" \
-c 'config_file=/etc/postgresql/postgresql.conf'
# can't mount postgresql.conf here because it prevents /var/lib/postgresql/data from being initialized
#-v "$srcdir/setup/postgresql.conf:/var/lib/postgresql/data/postgresql.conf" \
fi
wait_for_postgres_ready "$container_name"
echo
timestamp "linking shell profile for .psqlrc"
docker exec "$container_name" bash -c "cd /bash && setup/shell_link.sh &>/dev/null" || :
# yes expand now
# shellcheck disable=SC2064
trap "echo ERROR; echo; echo; [ -z '${DEBUG:-}' ] || docker logs '$container_name'" EXIT
if [ -n "${LOAD_SAMPLE_DB:-}" ]; then
dbname="${db##*/}"
dbname="${dbname%%.*}"
timestamp "loading $dbname database"
# psql -c doesn't allow mixing SQL and psql meta-commands, must pipe in
# create database if not exists equiv in postgres # \gexec executes each column returned as a SQL statement
echo "SELECT 'CREATE DATABASE $dbname' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '$dbname')\\gexec" |
docker exec -i -e PGOPTIONS="-c client_min_messages=WARNING" "$container_name" psql -U postgres
timestamp "loading data (this may take a minute)"
docker exec -e PGOPTIONS="-c client_min_messages=WARNING" "$container_name" psql -U postgres -q -d "$dbname" -f "/bash/${db##*/}.utf8"
timestamp "done"
echo >&2
fi
if has_terminal && [ -z "${DOCKER_NO_TERMINAL:-}" ]; then
cat <<EOF
$shell_description
EOF
fi
# cd to /sql to make sourcing easier without /sql/ path prefix
docker_exec_opts="-w /sql -i"
# allow non-interactive piped automation to avoid tty errors eg.
# for sql in postgres*.sql; do echo "source $sql"; done | postgres.sh
# normally you would just 'postgres.sh postgres*.sql' but this is used by postgres_test_scripts.sh
if has_terminal && [ -z "${DOCKER_NO_TERMINAL:-}" ]; then
docker_exec_opts+=" -t"
fi
# want opt splitting
# shellcheck disable=SC2086
docker exec $docker_exec_opts "$container_name" psql -U postgres
untrap
docker_rm_when_last_connection "$0" "$container_name"