about summary refs log tree commit diff
path: root/bskylink/tests/infra
diff options
context:
space:
mode:
Diffstat (limited to 'bskylink/tests/infra')
-rwxr-xr-xbskylink/tests/infra/_common.sh157
-rw-r--r--bskylink/tests/infra/docker-compose.yaml27
-rwxr-xr-xbskylink/tests/infra/with-test-db.sh9
3 files changed, 193 insertions, 0 deletions
diff --git a/bskylink/tests/infra/_common.sh b/bskylink/tests/infra/_common.sh
new file mode 100755
index 000000000..1587f5c70
--- /dev/null
+++ b/bskylink/tests/infra/_common.sh
@@ -0,0 +1,157 @@
+#!/usr/bin/env sh
+
+# Exit if any command fails
+set -e
+
+get_container_id() {
+  local compose_file=$1
+  local service=$2
+  if [ -z "${compose_file}" ] || [ -z "${service}" ]; then
+    echo "usage: get_container_id <compose_file> <service>"
+    exit 1
+  fi
+
+ # first line of jq normalizes for docker compose breaking change, see docker/compose#10958
+  docker compose --file $compose_file ps --format json --status running \
+    | jq -sc '.[] | if type=="array" then .[] else . end' | jq -s \
+    | jq -r '.[]? | select(.Service == "'${service}'") | .ID'
+}
+
+# Exports all environment variables
+export_env() {
+  export_pg_env
+}
+
+# Exports postgres environment variables
+export_pg_env() {
+  # Based on creds in compose.yaml
+  export PGPORT=5433
+  export PGHOST=localhost
+  export PGUSER=pg
+  export PGPASSWORD=password
+  export PGDATABASE=postgres
+  export DB_POSTGRES_URL="postgresql://pg:password@127.0.0.1:5433/postgres"
+}
+
+
+pg_clear() {
+  local pg_uri=$1
+
+  for schema_name in `psql "${pg_uri}" -c "SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT LIKE 'pg_%' AND schema_name NOT LIKE 'information_schema';" -t`; do
+    psql "${pg_uri}" -c "DROP SCHEMA \"${schema_name}\" CASCADE;"
+  done
+}
+
+pg_init() {
+  local pg_uri=$1
+
+  psql "${pg_uri}" -c "CREATE SCHEMA IF NOT EXISTS \"public\";"
+}
+
+main_native() {
+  local services=${SERVICES}
+  local postgres_url_env_var=`[[ $services == *"db_test"* ]] && echo "DB_TEST_POSTGRES_URL" || echo "DB_POSTGRES_URL"`
+
+  postgres_url="${!postgres_url_env_var}"
+
+  if [ -n "${postgres_url}" ]; then
+    echo "Using ${postgres_url_env_var} (${postgres_url}) to connect to postgres."
+    pg_init "${postgres_url}"
+  else
+    echo "Postgres connection string missing did you set ${postgres_url_env_var}?"
+    exit 1
+  fi
+
+  cleanup() {
+    local services=$@
+
+    if [ -n "${postgres_url}" ] && [[ $services == *"db_test"* ]]; then
+      pg_clear "${postgres_url}" &> /dev/null
+    fi
+  }
+
+  # trap SIGINT and performs cleanup
+  trap "on_sigint ${services}" INT
+  on_sigint() {
+    cleanup $@
+    exit $?
+  }
+
+  # Run the arguments as a command
+  DB_POSTGRES_URL="${postgres_url}" \
+  "$@"
+  code=$?
+
+  cleanup ${services}
+
+  exit ${code}
+}
+
+main_docker() {
+  # Expect a SERVICES env var to be set with the docker service names
+  local services=${SERVICES}
+
+  dir=$(dirname $0)
+  compose_file="${dir}/docker-compose.yaml"
+
+  # whether this particular script started the container(s)
+  started_container=false
+
+  # performs cleanup as necessary, i.e. taking down containers
+  # if this script started them
+  cleanup() {
+    local services=$@
+    echo # newline
+    if $started_container; then
+      docker compose --file $compose_file rm --force --stop --volumes ${services}
+    fi
+  }
+
+  # trap SIGINT and performs cleanup
+  trap "on_sigint ${services}" INT
+  on_sigint() {
+    cleanup $@
+    exit $?
+  }
+
+  # check if all services are running already
+  not_running=false
+  for service in $services; do
+    container_id=$(get_container_id $compose_file $service)
+    if [ -z $container_id ]; then
+      not_running=true
+      break
+    fi
+  done
+
+  # if any are missing, recreate all services
+  if $not_running; then
+    started_container=true
+    docker compose --file $compose_file up --wait --force-recreate ${services}
+  else
+    echo "all services ${services} are already running"
+  fi
+
+  # do not exit when following commands fail, so we can intercept exit code & tear down docker
+  set +e
+
+  # setup environment variables and run args
+  export_env
+  "$@"
+  # save return code for later
+  code=$?
+
+  # performs cleanup as necessary
+  cleanup ${services}
+  exit ${code}
+}
+
+# Main entry point
+main() {
+  if ! docker ps >/dev/null 2>&1; then
+    echo "Docker unavailable. Running on host."
+    main_native $@
+  else
+    main_docker $@
+  fi
+}
diff --git a/bskylink/tests/infra/docker-compose.yaml b/bskylink/tests/infra/docker-compose.yaml
new file mode 100644
index 000000000..4bc939e01
--- /dev/null
+++ b/bskylink/tests/infra/docker-compose.yaml
@@ -0,0 +1,27 @@
+version: '3.8'
+services:
+  # An ephermerally-stored postgres database for single-use test runs
+  db_test: &db_test
+    image: postgres:14.11-alpine
+    environment:
+      - POSTGRES_USER=pg
+      - POSTGRES_PASSWORD=password
+    ports:
+      - '5433:5432'
+    # Healthcheck ensures db is queryable when `docker-compose up --wait` completes
+    healthcheck:
+      test: 'pg_isready -U pg'
+      interval: 500ms
+      timeout: 10s
+      retries: 20
+  # A persistently-stored postgres database
+  db:
+    <<: *db_test
+    ports:
+      - '5432:5432'
+    healthcheck:
+      disable: true
+    volumes:
+      - link_db:/var/lib/postgresql/data
+volumes:
+  link_db:
diff --git a/bskylink/tests/infra/with-test-db.sh b/bskylink/tests/infra/with-test-db.sh
new file mode 100755
index 000000000..cc083491a
--- /dev/null
+++ b/bskylink/tests/infra/with-test-db.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env sh
+
+# Example usage:
+# ./with-test-db.sh psql postgresql://pg:password@localhost:5433/postgres -c 'select 1;'
+
+dir=$(dirname $0)
+. ${dir}/_common.sh
+
+SERVICES="db_test" main "$@"