From 841f148dba23ef72a39905579b35d1378faac7cb Mon Sep 17 00:00:00 2001 From: Tim Klein Date: Fri, 29 Oct 2021 12:02:18 -0400 Subject: [PATCH] [-] Be able to run `tox` locally (#243) * [-] Add PGUSER env var to testenv in tox - Also, add convenience script to run a PostgreSQL docker container for tox tests * [-] Add help text to postgres scripts * [-] Extend sleep for DB container, clarify postgis help text --- postgis-tests.sh | 127 ++++++++++++++++++++++++++++++++----- postgres-docker | 162 +++++++++++++++++++++++++++++++++++++++++++++++ tox.ini | 1 + 3 files changed, 274 insertions(+), 16 deletions(-) create mode 100755 postgres-docker diff --git a/postgis-tests.sh b/postgis-tests.sh index ffd62d6a..eec9f56a 100755 --- a/postgis-tests.sh +++ b/postgis-tests.sh @@ -1,23 +1,118 @@ -# Set a different port than postgres' default (in case the user is already running postgres locally) -export PGPORT=4111 -export PGUSER=postgres -export PGPASSWORD=postgres -export TEST_DB=postgis +#!/usr/bin/env bash +# -*- coding: utf-8 -*- +set -euo pipefail -# Run the postgis container -docker run --rm --name modelbakery -e POSTGRES_HOST_AUTH_METHOD=trust -p ${PGPORT}:5432 -d postgis/postgis:11-3.0 +### +### Run test suite against PostgreSQL DB with Postgis installed. +### +### This script will attempt to spin up a PostgreSQL Docker container against +### which the tests will be run. It will spin it down once the tests are finished, +### so if you are already running PostgreSQL locally, instead of using this +### script, simply run: +### +### # TEST_DB=postgis PGUSER=postgres python -m pytest +### +### This script uses the `python` on the current `$PATH`, but can be overridden +### by setting the `PYTHON_CLI` environment variable. +### +### Usage: +### +### ./postgis-tests.sh [-h|--help] +### +### Options: +### +### -h, --help print (this) help and exit +### -# Wait a few seconds so the DB container can start up -echo "Waiting for DB container..." -sleep 4s +function help { + # Print this file's contents, but only the lines that start + # with `###` (documentation lines, above). + sed -rn 's/^### ?//;T;p' "$0" +} -# Enable all of the extensions needed on the template1 database -docker exec modelbakery /bin/bash -c "psql template1 -c \"CREATE EXTENSION IF NOT EXISTS citext;\" -U postgres" -docker exec modelbakery /bin/bash -c "psql template1 -c \"CREATE EXTENSION IF NOT EXISTS hstore;\" -U postgres" -docker exec modelbakery /bin/bash -c "psql template1 -c \"CREATE EXTENSION IF NOT EXISTS postgis;\" -U postgres" +function HAS { + # Check if the system has a certain program ($1) installed. + # Output is silenced, but the function returns the result of + # the `command` statement. + command -v $1 > /dev/null 2>&1 + return $? +} + +# Script variables +PROJECT_ROOT="$(dirname $(readlink -f "$0"))" # Same level as this file + +# Dependency variables (e.g. PYTHON3_CLI=python3) +PYTHON=${PYTHON_CLI:-python} + +DEPS="$PYTHON" + +# Arg defaults +export PGPORT=${PGPORT:-5432} +export PGUSER=${PGUSER:-postgres} +export PGPASSWORD=${PGPASSWORD:-postgres} +export TEST_DB=${TEST_DB:-postgis} + +# Argument parsing using "getopt" +OPTIONS=h +LONGOPTS=help + +PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@") +PARSE_EXIT=$? + +if [[ $PARSE_EXIT -ne 0 ]]; then + exit $PARSE_EXIT +fi + +eval set -- "$PARSED" + +while true; do + case "$1" in + -h|--help) + help + shift + exit 0 + ;; + --) + shift + break + ;; + *) + echo "Error. Exiting." + exit 3 + ;; + esac +done + +# Grab the remaining positional argument(s) (not needed here) +# if [[ $# -ne 0 ]]; then +# POSITIONAL_1=$1 +# POSITIONAL_1=$2 +# else +# echo "Missing positional args?" +# exit 1 +# fi + +echo "Checking dependencies [$(echo ${DEPS} | sed 's/ /, /g')]..." +for dep in $DEPS; do + MISSING_DEP=false + + if ! HAS $dep; then + echo "You do not have '${dep}' installed, or it is not available on your PATH!" + echo "If '${dep}' is not what it is called on your system, set the ${dep^^}_CLI environment variable." + MISSING_DEP=true + fi + + if [[ $MISSING_DEP = true ]]; then + echo "Missing dependencies." + exit 1 + fi +done + +# Run the postgres container with all extensions installed +$PROJECT_ROOT/postgres-docker --port $PGPORT # Run the tests -python -m pytest +$PYTHON -m pytest # Spin down the postgis container -docker stop modelbakery +$PROJECT_ROOT/postgres-docker --kill diff --git a/postgres-docker b/postgres-docker new file mode 100755 index 00000000..d503d5a0 --- /dev/null +++ b/postgres-docker @@ -0,0 +1,162 @@ +#!/usr/bin/env bash +# -*- coding: utf-8 -*- +set -euo pipefail + +### +### Start a PostgreSQL docker container with the proper extensions installed +### for the full test suite. +### +### To kill an already running container, run the same command again with the +### `--kill` flag. +### +### Usage: +### +### ./postgres-docker [-h|--help] [-p|--port PORT] [-k|--kill] +### +### Options: +### +### -h, --help print (this) help and exit +### +### -p, --port PORT specify the host port to which PostgreSQL will be bound +### (defaults to 5432) +### +### -k, --kill given the container is already running, kill it +### + +function help { + # Print this file's contents, but only the lines that start + # with `###` (documentation lines, above). + sed -rn 's/^### ?//;T;p' "$0" +} + +function HAS { + # Check if the system has a certain program ($1) installed. + # Output is silenced, but the function returns the result of + # the `command` statement. + command -v $1 > /dev/null 2>&1 + return $? +} + +# Script variables +PROJECT_ROOT="$(dirname $(readlink -f "$0"))" # Same level as this file + +# Dependency variables (e.g. PYTHON3_CLI=python3) +DOCKER=${DOCKER_CLI:-docker} + +DEPS="$DOCKER" + +# Arg defaults +KILL=false +CONTAINER_NAME=model-bakery-postgres +PORT=${PGPORT:-5432} + +# Argument parsing using "getopt" +OPTIONS=h,k,p +LONGOPTS=help,kill,port + +PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@") +PARSE_EXIT=$? + +if [[ $PARSE_EXIT -ne 0 ]]; then + exit $PARSE_EXIT +fi + +eval set -- "$PARSED" + +while true; do + case "$1" in + -p|--port) + PGPORT=$2 + shift + ;; + -k|--kill) + KILL=true + shift + ;; + -h|--help) + help + shift + exit 0 + ;; + --) + shift + break + ;; + *) + echo "Error. Exiting." + exit 3 + ;; + esac +done + +# Grab the remaining positional argument(s) (not needed here) +# if [[ $# -ne 0 ]]; then +# POSITIONAL_1=$1 +# POSITIONAL_1=$2 +# else +# echo "Missing positional args?" +# exit 1 +# fi + +echo "Checking dependencies [$(echo ${DEPS} | sed 's/ /, /g')]..." +for dep in $DEPS; do + MISSING_DEP=false + + if ! HAS $dep; then + echo "You do not have '${dep}' installed, or it is not available on your PATH!" + echo "If '${dep}' is not what it is called on your system, set the ${dep^^}_CLI environment variable." + MISSING_DEP=true + fi + + if [[ $MISSING_DEP = true ]]; then + echo "Missing dependencies." + exit 1 + fi +done + +# Check if already running +if $DOCKER container inspect $CONTAINER_NAME > /dev/null 2>&1; then + RUNNING_ALREADY=true +else + RUNNING_ALREADY=false +fi + +# If stop desired, stop container +if [[ $KILL = true ]]; then + if [[ $RUNNING_ALREADY = true ]]; then + echo "Stopping '${CONTAINER_NAME}'..." + $DOCKER stop $CONTAINER_NAME + exit 0 + else + echo "Container '${CONTAINER_NAME}' is not currently running." + exit 1 + fi +fi + +# If running already, do nothing but check port +if [[ $RUNNING_ALREADY = true ]]; then + RUNNING_PORT=$($DOCKER container inspect $CONTAINER_NAME | grep -m 1 -Po "(?<=HostPort\": \")\d+") + + if [[ $RUNNING_PORT -ne $PORT ]]; then + echo "Container '${CONTAINER_NAME}' is already running, but on the wrong port!" + echo "Conainer is using port ${RUNNING_PORT} and the specified port is ${PORT}." + exit 1 + else + echo "Container '${CONTAINER_NAME}' is already running." + exit 0 + fi +fi + +# Start postgres container +echo "Starting '${CONTAINER_NAME}'..." + +$DOCKER run --rm --name ${CONTAINER_NAME} -e POSTGRES_HOST_AUTH_METHOD=trust -p ${PORT}:5432 -d postgis/postgis:11-3.0 + +# Wait a few seconds so the DB container can start up +echo "Waiting for DB container..." +sleep 5s + +# Enable all of the extensions needed on the template1 database +$DOCKER exec $CONTAINER_NAME /bin/bash -c "psql template1 -c \"CREATE EXTENSION IF NOT EXISTS citext;\" -U postgres" +$DOCKER exec $CONTAINER_NAME /bin/bash -c "psql template1 -c \"CREATE EXTENSION IF NOT EXISTS hstore;\" -U postgres" +$DOCKER exec $CONTAINER_NAME /bin/bash -c "psql template1 -c \"CREATE EXTENSION IF NOT EXISTS postgis;\" -U postgres" diff --git a/tox.ini b/tox.ini index c4fb5706..62f02d49 100644 --- a/tox.ini +++ b/tox.ini @@ -16,6 +16,7 @@ python = setenv = PYTHONPATH={toxinidir} postgresql: TEST_DB=postgis + postgresql: PGUSER=postgres sqlite: TEST_DB=sqlite sqlite: USE_TZ=True deps =