#!/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 " # 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="[] [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 # ensures version is correct before we kill any existing test env to switch versions timestamp "docker pull $docker_image:$version" docker_pull "$docker_image:$version" # kill existing if we have specified a different version than is running docker_ps_image_version="$(docker ps --filter "name=$container_name" --format '{{.Image}}')" if [ -n "$docker_ps_image_version" ] && [ "$docker_ps_image_version" != "$docker_image:$version" ]; then POSTGRES_RESTART=1 fi # remove existing non-running container so we can boot a new one if docker_ps_not_running "name=$container_name"; then POSTGRES_RESTART=1 fi if [ -n "${POSTGRES_RESTART:-}" ]; then timestamp "killing existing container:" docker rm -f "$container_name" 2>/dev/null || : fi if ! docker ps -qf name="$container_name" | grep -q .; then timestamp "booting PostgreSQL container from image '$docker_image:$version':" # defined in lib/dbshell.sh # shellcheck disable=SC2154 eval 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" \ SECONDS=0 max_secs=60 num_lines=50 # PostgreSQL 84: # # PostgreSQL stand-alone backend 8 # ... # LOG: database system is ready to accept connections # # # PostgreSQL 11.8: # # PostgreSQL init process complete; ready for start up. # ... # 2020-08-09 21:56:04.824 GMT [1] LOG: database system is ready to accept connections # while true; do timestamp 'waiting for postgres to be ready to accept connections before connecting psql...' if docker logs --tail "$num_lines" "$container_name" 2>&1 | grep -E -A "$num_lines" \ -e 'PostgreSQL init.*(ready|complete)' \ -e 'PostgreSQL stand-alone backend 8' | grep 'ready to accept connections'; then break fi sleep 1 if [ $SECONDS -gt $max_secs ]; then echo "PostgreSQL failed to become ready for connections within $max_secs secs, check logs (format may have changed):" echo docker logs "$container_name" exit 1 fi done echo fi 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 <