From e77e09c5d2d863d5b0500d08ee6afae7895da723 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 16 Apr 2022 17:17:46 +0200 Subject: [PATCH] postgresqlTestHook: init --- doc/hooks/index.xml | 10 +++ doc/hooks/postgresql-test-hook.section.md | 59 ++++++++++++++ doc/manual.xml | 1 + .../postgresql-test-hook/default.nix | 9 +++ .../postgresql-test-hook.sh | 79 +++++++++++++++++++ .../setup-hooks/postgresql-test-hook/test.nix | 27 +++++++ pkgs/top-level/all-packages.nix | 2 + 7 files changed, 187 insertions(+) create mode 100644 doc/hooks/index.xml create mode 100644 doc/hooks/postgresql-test-hook.section.md create mode 100644 pkgs/build-support/setup-hooks/postgresql-test-hook/default.nix create mode 100644 pkgs/build-support/setup-hooks/postgresql-test-hook/postgresql-test-hook.sh create mode 100644 pkgs/build-support/setup-hooks/postgresql-test-hook/test.nix diff --git a/doc/hooks/index.xml b/doc/hooks/index.xml new file mode 100644 index 00000000000..dca07819c62 --- /dev/null +++ b/doc/hooks/index.xml @@ -0,0 +1,10 @@ + + Hooks reference + + Nixpkgs has several hook functions that add augment the stdenv phases. + + + diff --git a/doc/hooks/postgresql-test-hook.section.md b/doc/hooks/postgresql-test-hook.section.md new file mode 100644 index 00000000000..077fac14ebb --- /dev/null +++ b/doc/hooks/postgresql-test-hook.section.md @@ -0,0 +1,59 @@ + +# `postgresqlTestHook` {#sec-postgresqlTestHook} + +This hook starts a PostgreSQL server during the `checkPhase`. Example: + +```nix +{ stdenv, postgresql, postgresqlTestHook }: +stdenv.mkDerivation { + + # ... + + checkInputs = [ + postgresql + postgresqlTestHook + ]; +} +``` + +If you use a custom `checkPhase`, remember to add the `runHook` calls: +```nix + checkPhase '' + runHook preCheck + + # ... your tests + + runHook postCheck + '' +``` + +## Variables {#sec-postgresqlTestHook-variables} + +The hook logic will read a number of variables and set them to a default value if unset or empty. + +Exported variables: + + - `PGDATA`: location of server files. + - `PGHOST`: location of UNIX domain socket directory; the default `host` in a connection string. + - `PGUSER`: user to create / log in with, default: `test_user`. + - `PGDATABASE`: database name, default: `test_db`. + +Bash-only variables: + + - `postgresqlTestUserOptions`: SQL options to use when creating the `$PGUSER` role, default: `LOGIN`. + - `postgresqlTestSetupSQL`: SQL commands to run as database administrator after startup, default: statements that create `$PGUSER` and `$PGDATABASE`. + - `postgresqlTestSetupCommands`: bash commands to run after database start, defaults to running `$postgresqlTestSetupSQL` as database administrator. + - `postgresqlEnableTCP`: set to `1` to enable TCP listening. Flaky; not recommended. + - `postgresqlStartCommands`: defaults to `pg_ctl start`. + +## TCP and the Nix sandbox {#sec-postgresqlTestHook-tcp} + +`postgresqlEnableTCP` relies on network sandboxing, which is not available on macOS and some custom Nix installations, resulting in flaky tests. +For this reason, it is disabled by default. + +The preferred solution is to make the test suite use a UNIX domain socket connection. This is the default behavior when no `host` connection parameter is provided. +Some test suites hardcode a value for `host` though, so a patch may be required. If you can upstream the patch, you can make `host` default to the `PGHOST` environment variable when set. Otherwise, you can patch it locally to omit the `host` connection string parameter altogether. + +::: {.note} +The error `libpq: failed (could not receive data from server: Connection refused` is generally an indication that the test suite is trying to connect through TCP. +::: diff --git a/doc/manual.xml b/doc/manual.xml index b43021d85ca..e49ae67ec94 100644 --- a/doc/manual.xml +++ b/doc/manual.xml @@ -27,6 +27,7 @@ + diff --git a/pkgs/build-support/setup-hooks/postgresql-test-hook/default.nix b/pkgs/build-support/setup-hooks/postgresql-test-hook/default.nix new file mode 100644 index 00000000000..d0031c93c10 --- /dev/null +++ b/pkgs/build-support/setup-hooks/postgresql-test-hook/default.nix @@ -0,0 +1,9 @@ +{ callPackage, makeSetupHook }: + +(makeSetupHook { + name = "postgresql-test-hook"; +} ./postgresql-test-hook.sh).overrideAttrs (o: { + passthru.tests = { + simple = callPackage ./test.nix { }; + }; +}) diff --git a/pkgs/build-support/setup-hooks/postgresql-test-hook/postgresql-test-hook.sh b/pkgs/build-support/setup-hooks/postgresql-test-hook/postgresql-test-hook.sh new file mode 100644 index 00000000000..041a3f56533 --- /dev/null +++ b/pkgs/build-support/setup-hooks/postgresql-test-hook/postgresql-test-hook.sh @@ -0,0 +1,79 @@ +preCheckHooks+=('postgresqlStart') +postCheckHooks+=('postgresqlStop') + + +postgresqlStart() { + + # Add default environment variable values + # + # Client variables: + # - https://www.postgresql.org/docs/current/libpq-envars.html + # + # Server variables: + # - only PGDATA: https://www.postgresql.org/docs/current/creating-cluster.html + + if [[ "${PGDATA:-}" == "" ]]; then + PGDATA="$NIX_BUILD_TOP/postgresql" + fi + export PGDATA + + if [[ "${PGHOST:-}" == "" ]]; then + mkdir -p "$NIX_BUILD_TOP/run/postgresql" + PGHOST="$NIX_BUILD_TOP/run/postgresql" + fi + export PGHOST + + if [[ "${PGUSER:-}" == "" ]]; then + PGUSER="test_user" + fi + export PGUSER + + if [[ "${PGDATABASE:-}" == "" ]]; then + PGDATABASE="test_db" + fi + export PGDATABASE + + if [[ "${postgresqlTestUserOptions:-}" == "" ]]; then + postgresqlTestUserOptions="LOGIN" + fi + + if [[ "${postgresqlTestSetupSQL:-}" == "" ]]; then + postgresqlTestSetupSQL="$(cat </dev/null; then + echo >&2 'initdb not found. Did you add postgresql to the checkInputs?' + false + fi + header 'initializing postgresql' + initdb -U postgres + + # Move the socket + echo "unix_socket_directories = '$NIX_BUILD_TOP/run/postgresql'" >>"$PGDATA/postgresql.conf" + + # TCP ports can be a problem in some sandboxes, + # so we disable tcp listening by default + if ! [[ "${postgresqlEnableTCP:-}" = 1 ]]; then + echo "listen_addresses = ''" >>"$PGDATA/postgresql.conf" + fi + + header 'starting postgresql' + eval "${postgresqlStartCommands:-pg_ctl start}" + + header 'setting up postgresql' + eval "$postgresqlTestSetupCommands" + +} + +postgresqlStop() { + header 'stopping postgresql' + pg_ctl stop +} diff --git a/pkgs/build-support/setup-hooks/postgresql-test-hook/test.nix b/pkgs/build-support/setup-hooks/postgresql-test-hook/test.nix new file mode 100644 index 00000000000..6d8ad6c8c7e --- /dev/null +++ b/pkgs/build-support/setup-hooks/postgresql-test-hook/test.nix @@ -0,0 +1,27 @@ +{ postgresql, postgresqlTestHook, stdenv }: + +stdenv.mkDerivation { + name = "postgresql-test-hook-test"; + buildInputs = [ postgresqlTestHook ]; + checkInputs = [ postgresql ]; + dontUnpack = true; + doCheck = true; + passAsFile = ["sql"]; + sql = '' + CREATE TABLE hello ( + message text + ); + INSERT INTO hello VALUES ('it '||'worked'); + SELECT * FROM hello; + ''; + checkPhase = '' + runHook preCheck + psql <$sqlPath | grep 'it worked' + TEST_RAN=1 + runHook postCheck + ''; + installPhase = '' + [[ $TEST_RAN == 1 ]] + touch $out + ''; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 615bf750169..12bec9613e6 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -21965,6 +21965,8 @@ with pkgs; postgresql_jdbc = callPackage ../development/java-modules/postgresql_jdbc { }; + postgresqlTestHook = callPackage ../build-support/setup-hooks/postgresql-test-hook { }; + prom2json = callPackage ../servers/monitoring/prometheus/prom2json.nix { }; prometheus = callPackage ../servers/monitoring/prometheus { }; prometheus-alertmanager = callPackage ../servers/monitoring/prometheus/alertmanager.nix { };