From ff254c7120357c1a05af7674f964fe42e6a0cea1 Mon Sep 17 00:00:00 2001 From: r-vdp Date: Tue, 27 Jan 2026 12:02:38 +0100 Subject: [PATCH] nixos/tests/postgresql: migrate from handleTest to runTest See: https://github.com/NixOS/nixpkgs/issues/386873 --- nixos/tests/all-tests.nix | 2 +- nixos/tests/postgresql/anonymizer.nix | 179 +++---- nixos/tests/postgresql/default.nix | 14 +- nixos/tests/postgresql/pgjwt.nix | 79 +-- nixos/tests/postgresql/postgresql-jit.nix | 75 +-- .../postgresql/postgresql-replication.nix | 194 ++++---- .../postgresql/postgresql-tls-client-cert.nix | 254 +++++----- .../postgresql/postgresql-wal-receiver.nix | 183 +++---- nixos/tests/postgresql/postgresql.nix | 467 +++++++++--------- nixos/tests/postgresql/wal2json.nix | 67 +-- 10 files changed, 766 insertions(+), 748 deletions(-) diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index ee1c593430c6..bb9becbc78ee 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1283,7 +1283,7 @@ in { }; postfix-tlspol = runTest ./postfix-tlspol.nix; postgres-websockets = runTest ./postgres-websockets.nix; - postgresql = handleTest ./postgresql { }; + postgresql = import ./postgresql { inherit runTest pkgs; }; postgrest = runTest ./postgrest.nix; power-profiles-daemon = runTest ./power-profiles-daemon.nix; powerdns = runTest ./powerdns.nix; diff --git a/nixos/tests/postgresql/anonymizer.nix b/nixos/tests/postgresql/anonymizer.nix index a5853e5ed533..4b37a4f8be5d 100644 --- a/nixos/tests/postgresql/anonymizer.nix +++ b/nixos/tests/postgresql/anonymizer.nix @@ -1,114 +1,115 @@ { - pkgs, - makeTest, + runTest, genTests, + ... }: let - inherit (pkgs) lib; - makeTestFor = package: - makeTest { - name = "postgresql_anonymizer-${package.name}"; - meta.maintainers = [ - lib.maintainers.leona - lib.maintainers.osnyx - ]; + runTest ( + { lib, pkgs, ... }: + { + name = "postgresql_anonymizer-${package.name}"; + meta.maintainers = [ + lib.maintainers.leona + lib.maintainers.osnyx + ]; - nodes.machine = - { pkgs, ... }: - { - environment.systemPackages = [ (pkgs.pg-dump-anon.override { postgresql = package; }) ]; - services.postgresql = { - inherit package; - enable = true; - extensions = ps: [ ps.anonymizer ]; - settings.shared_preload_libraries = [ "anon" ]; + nodes.machine = + { pkgs, ... }: + { + environment.systemPackages = [ (pkgs.pg-dump-anon.override { postgresql = package; }) ]; + services.postgresql = { + inherit package; + enable = true; + extensions = ps: [ ps.anonymizer ]; + settings.shared_preload_libraries = [ "anon" ]; + }; }; - }; - testScript = '' - start_all() - machine.wait_for_unit("multi-user.target") - machine.wait_for_unit("postgresql.target") + testScript = '' + start_all() + machine.wait_for_unit("multi-user.target") + machine.wait_for_unit("postgresql.target") - with subtest("Setup"): - machine.succeed("sudo -u postgres psql --command 'create database demo'") - machine.succeed( - "sudo -u postgres psql -d demo -f ${pkgs.writeText "init.sql" '' - create extension anon cascade; - select anon.init(); - create table player(id serial, name text, points int); - insert into player(id,name,points) values (1,'Foo', 23); - insert into player(id,name,points) values (2,'Bar',42); - security label for anon on column player.name is 'MASKED WITH FUNCTION anon.fake_last_name()'; - security label for anon on column player.points is 'MASKED WITH VALUE NULL'; - ''}" - ) + with subtest("Setup"): + machine.succeed("sudo -u postgres psql --command 'create database demo'") + machine.succeed( + "sudo -u postgres psql -d demo -f ${pkgs.writeText "init.sql" '' + create extension anon cascade; + select anon.init(); + create table player(id serial, name text, points int); + insert into player(id,name,points) values (1,'Foo', 23); + insert into player(id,name,points) values (2,'Bar',42); + security label for anon on column player.name is 'MASKED WITH FUNCTION anon.fake_last_name()'; + security label for anon on column player.points is 'MASKED WITH VALUE NULL'; + ''}" + ) - def get_player_table_contents(): - return [ - x.split(',') for x in machine.succeed("sudo -u postgres psql -d demo --csv --command 'select * from player'").splitlines()[1:] - ] + def get_player_table_contents(): + return [ + x.split(',') for x in machine.succeed("sudo -u postgres psql -d demo --csv --command 'select * from player'").splitlines()[1:] + ] - def check_anonymized_row(row, id, original_name): - t.assertEqual(row[0], id) - t.assertNotEqual(row[1], original_name) - t.assertFalse(bool(row[2])) + def check_anonymized_row(row, id, original_name): + t.assertEqual(row[0], id) + t.assertNotEqual(row[1], original_name) + t.assertFalse(bool(row[2])) - def find_xsv_in_dump(dump, sep=','): - """ - Expecting to find a CSV (for pg_dump_anon) or TSV (for pg_dump) structure, looking like + def find_xsv_in_dump(dump, sep=','): + """ + Expecting to find a CSV (for pg_dump_anon) or TSV (for pg_dump) structure, looking like - COPY public.player ... - 1,Shields, - 2,Salazar, - \\. + COPY public.player ... + 1,Shields, + 2,Salazar, + \\. - in the given dump (the commas are tabs in case of pg_dump). - Extract the CSV lines and split by `sep`. - """ + in the given dump (the commas are tabs in case of pg_dump). + Extract the CSV lines and split by `sep`. + """ - try: - from itertools import dropwhile, takewhile - return [x.split(sep) for x in list(takewhile( - lambda x: x != "\\.", - dropwhile( - lambda x: not x.startswith("COPY public.player"), - dump.splitlines() - ) - ))[1:]] - except: - print(f"Dump to process: {dump}") - raise + try: + from itertools import dropwhile, takewhile + return [x.split(sep) for x in list(takewhile( + lambda x: x != "\\.", + dropwhile( + lambda x: not x.startswith("COPY public.player"), + dump.splitlines() + ) + ))[1:]] + except: + print(f"Dump to process: {dump}") + raise - def check_original_data(output): - t.assertEqual(output[0], ["1", "Foo", "23"]) - t.assertEqual(output[1], ["2", "Bar", "42"]) + def check_original_data(output): + t.assertEqual(output[0], ["1", "Foo", "23"]) + t.assertEqual(output[1], ["2", "Bar", "42"]) - def check_anonymized_rows(output): - check_anonymized_row(output[0], '1', 'Foo') - check_anonymized_row(output[1], '2', 'Bar') + def check_anonymized_rows(output): + check_anonymized_row(output[0], '1', 'Foo') + check_anonymized_row(output[1], '2', 'Bar') - with subtest("Check initial state"): - check_original_data(get_player_table_contents()) + with subtest("Check initial state"): + check_original_data(get_player_table_contents()) - with subtest("Anonymous dumps"): - check_original_data(find_xsv_in_dump( - machine.succeed("sudo -u postgres pg_dump demo"), - sep='\t' - )) - check_anonymized_rows(find_xsv_in_dump( - machine.succeed("sudo -u postgres pg_dump_anon -U postgres -h /run/postgresql -d demo"), - sep=',' - )) + with subtest("Anonymous dumps"): + check_original_data(find_xsv_in_dump( + machine.succeed("sudo -u postgres pg_dump demo"), + sep='\t' + )) + check_anonymized_rows(find_xsv_in_dump( + machine.succeed("sudo -u postgres pg_dump_anon -U postgres -h /run/postgresql -d demo"), + sep=',' + )) - with subtest("Anonymize"): - machine.succeed("sudo -u postgres psql -d demo --command 'select anon.anonymize_database();'") - check_anonymized_rows(get_player_table_contents()) - ''; - }; + with subtest("Anonymize"): + machine.succeed("sudo -u postgres psql -d demo --command 'select anon.anonymize_database();'") + check_anonymized_rows(get_player_table_contents()) + ''; + } + ); in genTests { inherit makeTestFor; diff --git a/nixos/tests/postgresql/default.nix b/nixos/tests/postgresql/default.nix index a8b03a29280b..a5d363a709ee 100644 --- a/nixos/tests/postgresql/default.nix +++ b/nixos/tests/postgresql/default.nix @@ -1,11 +1,8 @@ { - system ? builtins.currentSystem, - config ? { }, - pkgs ? import ../../.. { inherit system config; }, + runTest, + pkgs, }: -with import ../../lib/testing-python.nix { inherit system pkgs; }; - let inherit (pkgs.lib) recurseIntoAttrs @@ -25,7 +22,12 @@ let } ); - importWithArgs = path: import path { inherit pkgs makeTest genTests; }; + importWithArgs = + path: + import path { + inherit runTest genTests; + inherit (pkgs) lib; + }; in { # postgresql diff --git a/nixos/tests/postgresql/pgjwt.nix b/nixos/tests/postgresql/pgjwt.nix index 6fc43d771b92..b16efeb97ad6 100644 --- a/nixos/tests/postgresql/pgjwt.nix +++ b/nixos/tests/postgresql/pgjwt.nix @@ -1,51 +1,52 @@ { - pkgs, - makeTest, + runTest, genTests, + ... }: let - inherit (pkgs) lib; - makeTestFor = package: - makeTest { - name = "pgjwt-${package.name}"; - meta = with lib.maintainers; { - maintainers = [ - spinus - ]; - }; - - nodes.master = - { ... }: - { - services.postgresql = { - inherit package; - enable = true; - extensions = - ps: with ps; [ - pgjwt - pgtap - ]; - }; + runTest ( + { lib, pkgs, ... }: + { + name = "pgjwt-${package.name}"; + meta = with lib.maintainers; { + maintainers = [ + spinus + ]; }; - testScript = - { nodes, ... }: - let - sqlSU = "${nodes.master.services.postgresql.superUser}"; - pgProve = "${pkgs.perlPackages.TAPParserSourceHandlerpgTAP}"; - inherit (nodes.master.services.postgresql.package.pkgs) pgjwt; - in - '' - start_all() - master.wait_for_unit("postgresql.target") - master.succeed( - "${pkgs.sudo}/bin/sudo -u ${sqlSU} ${pgProve}/bin/pg_prove -d postgres -v -f ${pgjwt.src}/test.sql" - ) - ''; - }; + nodes.master = + { ... }: + { + services.postgresql = { + inherit package; + enable = true; + extensions = + ps: with ps; [ + pgjwt + pgtap + ]; + }; + }; + + testScript = + { nodes, ... }: + let + sqlSU = "${nodes.master.services.postgresql.superUser}"; + pgProve = "${pkgs.perlPackages.TAPParserSourceHandlerpgTAP}"; + inherit (nodes.master.services.postgresql.package.pkgs) pgjwt; + in + '' + start_all() + master.wait_for_unit("postgresql.target") + master.succeed( + "${pkgs.sudo}/bin/sudo -u ${sqlSU} ${pgProve}/bin/pg_prove -d postgres -v -f ${pgjwt.src}/test.sql" + ) + ''; + } + ); in genTests { inherit makeTestFor; diff --git a/nixos/tests/postgresql/postgresql-jit.nix b/nixos/tests/postgresql/postgresql-jit.nix index 347534c8bd85..de62b355d62b 100644 --- a/nixos/tests/postgresql/postgresql-jit.nix +++ b/nixos/tests/postgresql/postgresql-jit.nix @@ -1,53 +1,54 @@ { - pkgs, - makeTest, + runTest, genTests, + ... }: let - inherit (pkgs) lib; - makeTestFor = package: - makeTest { - name = "postgresql-jit-${package.name}"; - meta.maintainers = with lib.maintainers; [ ma27 ]; + runTest ( + { lib, pkgs, ... }: + { + name = "postgresql-jit-${package.name}"; + meta.maintainers = with lib.maintainers; [ ma27 ]; - nodes.machine = - { pkgs, ... }: - { - services.postgresql = { - inherit package; - enable = true; - enableJIT = true; - initialScript = pkgs.writeText "init.sql" '' - create table demo (id int); - insert into demo (id) select generate_series(1, 5); - ''; + nodes.machine = + { pkgs, ... }: + { + services.postgresql = { + inherit package; + enable = true; + enableJIT = true; + initialScript = pkgs.writeText "init.sql" '' + create table demo (id int); + insert into demo (id) select generate_series(1, 5); + ''; + }; }; - }; - testScript = '' - machine.start() - machine.wait_for_unit("postgresql.target") + testScript = '' + machine.start() + machine.wait_for_unit("postgresql.target") - with subtest("JIT is enabled"): - machine.succeed("sudo -u postgres psql <<<'show jit;' | grep 'on'") + with subtest("JIT is enabled"): + machine.succeed("sudo -u postgres psql <<<'show jit;' | grep 'on'") - with subtest("Test JIT works fine"): - output = machine.succeed( - "cat ${pkgs.writeText "test.sql" '' - set jit_above_cost = 1; - EXPLAIN ANALYZE SELECT CONCAT('jit result = ', SUM(id)) FROM demo; - SELECT CONCAT('jit result = ', SUM(id)) from demo; - ''} | sudo -u postgres psql" - ) - t.assertIn("JIT:", output) - t.assertIn("jit result = 15", output) + with subtest("Test JIT works fine"): + output = machine.succeed( + "cat ${pkgs.writeText "test.sql" '' + set jit_above_cost = 1; + EXPLAIN ANALYZE SELECT CONCAT('jit result = ', SUM(id)) FROM demo; + SELECT CONCAT('jit result = ', SUM(id)) from demo; + ''} | sudo -u postgres psql" + ) + t.assertIn("JIT:", output) + t.assertIn("jit result = 15", output) - machine.shutdown() - ''; - }; + machine.shutdown() + ''; + } + ); in genTests { inherit makeTestFor; diff --git a/nixos/tests/postgresql/postgresql-replication.nix b/nixos/tests/postgresql/postgresql-replication.nix index b828c2d7bb49..6df0becdf115 100644 --- a/nixos/tests/postgresql/postgresql-replication.nix +++ b/nixos/tests/postgresql/postgresql-replication.nix @@ -1,125 +1,127 @@ { - pkgs, - makeTest, + runTest, genTests, + ... }: let - inherit (pkgs) lib; makeTestFor = package: - makeTest { - name = "postgresql-replication-${package.name}"; - meta.maintainers = with lib.maintainers; [ bouk ]; + runTest ( + { lib, ... }: + { + name = "postgresql-replication-${package.name}"; + meta.maintainers = with lib.maintainers; [ bouk ]; - nodes = { - primary = - { ... }: - { - services.postgresql = { - inherit package; - enable = true; - enableTCPIP = true; - settings = { - wal_level = "replica"; - max_wal_senders = 10; - max_replication_slots = 10; + nodes = { + primary = + { ... }: + { + services.postgresql = { + inherit package; + enable = true; + enableTCPIP = true; + settings = { + wal_level = "replica"; + max_wal_senders = 10; + max_replication_slots = 10; + }; + authentication = '' + local replication postgres peer + host replication replication all trust + ''; + ensureUsers = [ + { + name = "replication"; + ensureClauses.replication = true; + } + ]; }; - authentication = '' - local replication postgres peer - host replication replication all trust - ''; - ensureUsers = [ - { - name = "replication"; - ensureClauses.replication = true; - } - ]; + networking.firewall.allowedTCPPorts = [ 5432 ]; }; - networking.firewall.allowedTCPPorts = [ 5432 ]; - }; - replica = - { nodes, ... }: - { - services.postgresql = { - inherit package; - enable = true; - settings = { - hot_standby = "on"; - primary_conninfo = "host=${nodes.primary.networking.primaryIPAddress} user=replication"; - primary_slot_name = "replica_slot"; + replica = + { nodes, ... }: + { + services.postgresql = { + inherit package; + enable = true; + settings = { + hot_standby = "on"; + primary_conninfo = "host=${nodes.primary.networking.primaryIPAddress} user=replication"; + primary_slot_name = "replica_slot"; + }; }; }; - }; - }; + }; - testScript = '' - start_all() - primary.wait_for_unit("postgresql.target") + testScript = '' + start_all() + primary.wait_for_unit("postgresql.target") - primary.succeed( - "sudo -u postgres psql -c \"SELECT * FROM pg_create_physical_replication_slot('replica_slot');\"" - ) + primary.succeed( + "sudo -u postgres psql -c \"SELECT * FROM pg_create_physical_replication_slot('replica_slot');\"" + ) - primary.succeed( - "sudo -u postgres pg_basebackup -D /tmp/basebackup -S replica_slot -X stream" - ) - primary.succeed("tar -C /tmp -cf /tmp/shared/basebackup.tar basebackup") + primary.succeed( + "sudo -u postgres pg_basebackup -D /tmp/basebackup -S replica_slot -X stream" + ) + primary.succeed("tar -C /tmp -cf /tmp/shared/basebackup.tar basebackup") - replica.wait_for_unit("postgresql.target") - replica.succeed("systemctl stop postgresql") + replica.wait_for_unit("postgresql.target") + replica.succeed("systemctl stop postgresql") - replica_data_dir = "/var/lib/postgresql/${package.psqlSchema}" - replica.succeed(f"rm -rf {replica_data_dir}") - replica.succeed(f"mkdir -p {replica_data_dir}") - replica.succeed(f"tar -C {replica_data_dir} --strip-components=1 -xf /tmp/shared/basebackup.tar") - replica.succeed(f"touch {replica_data_dir}/standby.signal") - replica.succeed(f"chown -R postgres:postgres {replica_data_dir}") - replica.succeed(f"chmod 700 {replica_data_dir}") + replica_data_dir = "/var/lib/postgresql/${package.psqlSchema}" + replica.succeed(f"rm -rf {replica_data_dir}") + replica.succeed(f"mkdir -p {replica_data_dir}") + replica.succeed(f"tar -C {replica_data_dir} --strip-components=1 -xf /tmp/shared/basebackup.tar") + replica.succeed(f"touch {replica_data_dir}/standby.signal") + replica.succeed(f"chown -R postgres:postgres {replica_data_dir}") + replica.succeed(f"chmod 700 {replica_data_dir}") - replica.succeed("systemctl start postgresql") - replica.wait_for_unit("postgresql.target") + replica.succeed("systemctl start postgresql") + replica.wait_for_unit("postgresql.target") - replica.wait_until_succeeds( - "sudo -u postgres psql -tAc 'SELECT pg_is_in_recovery();' | grep t" - ) + replica.wait_until_succeeds( + "sudo -u postgres psql -tAc 'SELECT pg_is_in_recovery();' | grep t" + ) - primary.succeed( - "sudo -u postgres psql -c 'CREATE TABLE test_replication (id serial PRIMARY KEY, data text);'" - ) - primary.succeed( - "sudo -u postgres psql -c \"INSERT INTO test_replication (data) VALUES ('hello');\"" - ) + primary.succeed( + "sudo -u postgres psql -c 'CREATE TABLE test_replication (id serial PRIMARY KEY, data text);'" + ) + primary.succeed( + "sudo -u postgres psql -c \"INSERT INTO test_replication (data) VALUES ('hello');\"" + ) - replica.wait_until_succeeds( - "sudo -u postgres psql -c 'SELECT * FROM test_replication;' | grep hello", - timeout=30 - ) + replica.wait_until_succeeds( + "sudo -u postgres psql -c 'SELECT * FROM test_replication;' | grep hello", + timeout=30 + ) - with subtest("Verify replica is in recovery mode"): - result = replica.succeed("sudo -u postgres psql -tAc 'SELECT pg_is_in_recovery();'") - t.assertEqual(result.strip(), "t") + with subtest("Verify replica is in recovery mode"): + result = replica.succeed("sudo -u postgres psql -tAc 'SELECT pg_is_in_recovery();'") + t.assertEqual(result.strip(), "t") - with subtest("Verify replication slot is active"): - result = primary.succeed( - "sudo -u postgres psql -tAc \"SELECT active FROM pg_replication_slots WHERE slot_name = 'replica_slot';\"" - ) - t.assertEqual(result.strip(), "t") + with subtest("Verify replication slot is active"): + result = primary.succeed( + "sudo -u postgres psql -tAc \"SELECT active FROM pg_replication_slots WHERE slot_name = 'replica_slot';\"" + ) + t.assertEqual(result.strip(), "t") - with subtest("Insert more data and verify replication"): - primary.succeed( - "sudo -u postgres psql -c \"INSERT INTO test_replication (data) VALUES ('world');\"" - ) - replica.wait_until_succeeds( - "sudo -u postgres psql -c 'SELECT * FROM test_replication;' | grep world", - timeout=30 - ) + with subtest("Insert more data and verify replication"): + primary.succeed( + "sudo -u postgres psql -c \"INSERT INTO test_replication (data) VALUES ('world');\"" + ) + replica.wait_until_succeeds( + "sudo -u postgres psql -c 'SELECT * FROM test_replication;' | grep world", + timeout=30 + ) - primary.shutdown() - replica.shutdown() - ''; - }; + primary.shutdown() + replica.shutdown() + ''; + } + ); in genTests { inherit makeTestFor; } diff --git a/nixos/tests/postgresql/postgresql-tls-client-cert.nix b/nixos/tests/postgresql/postgresql-tls-client-cert.nix index acb531780f76..19af512e28ff 100644 --- a/nixos/tests/postgresql/postgresql-tls-client-cert.nix +++ b/nixos/tests/postgresql/postgresql-tls-client-cert.nix @@ -1,145 +1,147 @@ { - pkgs, - makeTest, + runTest, genTests, + ... }: let - inherit (pkgs) lib; - - runWithOpenSSL = - file: cmd: - pkgs.runCommand file { - buildInputs = [ pkgs.openssl ]; - } cmd; - caKey = runWithOpenSSL "ca.key" "openssl ecparam -name prime256v1 -genkey -noout -out $out"; - caCert = runWithOpenSSL "ca.crt" '' - openssl req -new -x509 -sha256 -key ${caKey} -out $out -subj "/CN=test.example" -days 36500 - ''; - serverKey = runWithOpenSSL "server.key" "openssl ecparam -name prime256v1 -genkey -noout -out $out"; - serverKeyPath = "/var/lib/postgresql"; - serverCert = runWithOpenSSL "server.crt" '' - openssl req -new -sha256 -key ${serverKey} -out server.csr -subj "/CN=db.test.example" - openssl x509 -req -in server.csr -CA ${caCert} -CAkey ${caKey} \ - -CAcreateserial -out $out -days 36500 -sha256 - ''; - clientKey = runWithOpenSSL "client.key" "openssl ecparam -name prime256v1 -genkey -noout -out $out"; - clientCert = runWithOpenSSL "client.crt" '' - openssl req -new -sha256 -key ${clientKey} -out client.csr -subj "/CN=test" - openssl x509 -req -in client.csr -CA ${caCert} -CAkey ${caKey} \ - -CAcreateserial -out $out -days 36500 -sha256 - ''; - clientKeyPath = "/root"; - makeTestFor = package: - makeTest { - name = "postgresql-tls-client-cert-${package.name}"; - meta.maintainers = with lib.maintainers; [ erictapen ]; + runTest ( + { lib, pkgs, ... }: + let + runWithOpenSSL = + file: cmd: + pkgs.runCommand file { + buildInputs = [ pkgs.openssl ]; + } cmd; + caKey = runWithOpenSSL "ca.key" "openssl ecparam -name prime256v1 -genkey -noout -out $out"; + caCert = runWithOpenSSL "ca.crt" '' + openssl req -new -x509 -sha256 -key ${caKey} -out $out -subj "/CN=test.example" -days 36500 + ''; + serverKey = runWithOpenSSL "server.key" "openssl ecparam -name prime256v1 -genkey -noout -out $out"; + serverKeyPath = "/var/lib/postgresql"; + serverCert = runWithOpenSSL "server.crt" '' + openssl req -new -sha256 -key ${serverKey} -out server.csr -subj "/CN=db.test.example" + openssl x509 -req -in server.csr -CA ${caCert} -CAkey ${caKey} \ + -CAcreateserial -out $out -days 36500 -sha256 + ''; + clientKey = runWithOpenSSL "client.key" "openssl ecparam -name prime256v1 -genkey -noout -out $out"; + clientCert = runWithOpenSSL "client.crt" '' + openssl req -new -sha256 -key ${clientKey} -out client.csr -subj "/CN=test" + openssl x509 -req -in client.csr -CA ${caCert} -CAkey ${caKey} \ + -CAcreateserial -out $out -days 36500 -sha256 + ''; + clientKeyPath = "/root"; + in + { + name = "postgresql-tls-client-cert-${package.name}"; + meta.maintainers = with lib.maintainers; [ erictapen ]; - nodes.server = - { ... }: - { - systemd.services.create-keys = { - wantedBy = [ "postgresql.target" ]; + nodes.server = + { ... }: + { + systemd.services.create-keys = { + wantedBy = [ "postgresql.target" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + + script = '' + mkdir -p '${serverKeyPath}' + cp '${serverKey}' '${serverKeyPath}/server.key' + chown postgres:postgres '${serverKeyPath}/server.key' + chmod 600 '${serverKeyPath}/server.key' + ''; }; - - script = '' - mkdir -p '${serverKeyPath}' - cp '${serverKey}' '${serverKeyPath}/server.key' - chown postgres:postgres '${serverKeyPath}/server.key' - chmod 600 '${serverKeyPath}/server.key' - ''; - }; - services.postgresql = { - inherit package; - enable = true; - enableTCPIP = true; - ensureUsers = [ - { - name = "test"; - ensureDBOwnership = true; - } - ]; - ensureDatabases = [ "test" ]; - settings = { - ssl = "on"; - ssl_ca_file = toString caCert; - ssl_cert_file = toString serverCert; - ssl_key_file = "${serverKeyPath}/server.key"; - }; - authentication = '' - hostssl test test ::/0 cert clientcert=verify-full - ''; - }; - networking = { - interfaces.eth1 = { - ipv6.addresses = [ + services.postgresql = { + inherit package; + enable = true; + enableTCPIP = true; + ensureUsers = [ { - address = "fc00::1"; - prefixLength = 120; + name = "test"; + ensureDBOwnership = true; } ]; + ensureDatabases = [ "test" ]; + settings = { + ssl = "on"; + ssl_ca_file = toString caCert; + ssl_cert_file = toString serverCert; + ssl_key_file = "${serverKeyPath}/server.key"; + }; + authentication = '' + hostssl test test ::/0 cert clientcert=verify-full + ''; }; - firewall.allowedTCPPorts = [ 5432 ]; - }; - }; - - nodes.client = - { ... }: - { - systemd.services.create-keys = { - wantedBy = [ "multi-user.target" ]; - - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - }; - - script = '' - mkdir -p '${clientKeyPath}' - cp '${clientKey}' '${clientKeyPath}/client.key' - chown root:root '${clientKeyPath}/client.key' - chmod 600 '${clientKeyPath}/client.key' - ''; - }; - environment = { - variables = { - PGHOST = "db.test.example"; - PGPORT = "5432"; - PGDATABASE = "test"; - PGUSER = "test"; - PGSSLMODE = "verify-full"; - PGSSLCERT = clientCert; - PGSSLKEY = "${clientKeyPath}/client.key"; - PGSSLROOTCERT = caCert; - }; - systemPackages = [ package ]; - }; - networking = { - interfaces.eth1 = { - ipv6.addresses = [ - { - address = "fc00::2"; - prefixLength = 120; - } - ]; - }; - hosts = { - "fc00::1" = [ "db.test.example" ]; + networking = { + interfaces.eth1 = { + ipv6.addresses = [ + { + address = "fc00::1"; + prefixLength = 120; + } + ]; + }; + firewall.allowedTCPPorts = [ 5432 ]; }; }; - }; - testScript = '' - server.wait_for_unit("multi-user.target") - client.wait_for_unit("multi-user.target") - client.succeed("psql -c \"SELECT 1;\"") - ''; - }; + nodes.client = + { ... }: + { + systemd.services.create-keys = { + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + + script = '' + mkdir -p '${clientKeyPath}' + cp '${clientKey}' '${clientKeyPath}/client.key' + chown root:root '${clientKeyPath}/client.key' + chmod 600 '${clientKeyPath}/client.key' + ''; + }; + environment = { + variables = { + PGHOST = "db.test.example"; + PGPORT = "5432"; + PGDATABASE = "test"; + PGUSER = "test"; + PGSSLMODE = "verify-full"; + PGSSLCERT = clientCert; + PGSSLKEY = "${clientKeyPath}/client.key"; + PGSSLROOTCERT = caCert; + }; + systemPackages = [ package ]; + }; + networking = { + interfaces.eth1 = { + ipv6.addresses = [ + { + address = "fc00::2"; + prefixLength = 120; + } + ]; + }; + hosts = { + "fc00::1" = [ "db.test.example" ]; + }; + }; + }; + + testScript = '' + server.wait_for_unit("multi-user.target") + client.wait_for_unit("multi-user.target") + client.succeed("psql -c \"SELECT 1;\"") + ''; + } + ); in genTests { inherit makeTestFor; } diff --git a/nixos/tests/postgresql/postgresql-wal-receiver.nix b/nixos/tests/postgresql/postgresql-wal-receiver.nix index c233a582546c..f929596cf614 100644 --- a/nixos/tests/postgresql/postgresql-wal-receiver.nix +++ b/nixos/tests/postgresql/postgresql-wal-receiver.nix @@ -1,111 +1,112 @@ { - pkgs, - makeTest, + runTest, genTests, + ... }: let - inherit (pkgs) lib; - makeTestFor = package: - let - postgresqlDataDir = "/var/lib/postgresql/${package.psqlSchema}"; - replicationUser = "wal_receiver_user"; - replicationSlot = "wal_receiver_slot"; - replicationConn = "postgresql://${replicationUser}@localhost"; - baseBackupDir = "/var/cache/wals/pg_basebackup"; - walBackupDir = "/var/cache/wals/pg_wal"; - recoveryFile = pkgs.writeTextDir "recovery.signal" ""; - in - makeTest { - name = "postgresql-wal-receiver-${package.name}"; - meta.maintainers = with lib.maintainers; [ euxane ]; + runTest ( + { lib, pkgs, ... }: + let + postgresqlDataDir = "/var/lib/postgresql/${package.psqlSchema}"; + replicationUser = "wal_receiver_user"; + replicationSlot = "wal_receiver_slot"; + replicationConn = "postgresql://${replicationUser}@localhost"; + baseBackupDir = "/var/cache/wals/pg_basebackup"; + walBackupDir = "/var/cache/wals/pg_wal"; + recoveryFile = pkgs.writeTextDir "recovery.signal" ""; + in + { + name = "postgresql-wal-receiver-${package.name}"; + meta.maintainers = with lib.maintainers; [ euxane ]; - nodes.machine = - { ... }: - { - systemd.tmpfiles.rules = [ - "d /var/cache/wals 0750 postgres postgres - -" - ]; + nodes.machine = + { ... }: + { + systemd.tmpfiles.rules = [ + "d /var/cache/wals 0750 postgres postgres - -" + ]; - services.postgresql = { - inherit package; - enable = true; - settings = { - max_replication_slots = 10; - max_wal_senders = 10; - recovery_end_command = "touch recovery.done"; - restore_command = "cp ${walBackupDir}/%f %p"; - wal_level = "archive"; # alias for replica on pg >= 9.6 + services.postgresql = { + inherit package; + enable = true; + settings = { + max_replication_slots = 10; + max_wal_senders = 10; + recovery_end_command = "touch recovery.done"; + restore_command = "cp ${walBackupDir}/%f %p"; + wal_level = "archive"; # alias for replica on pg >= 9.6 + }; + authentication = '' + host replication ${replicationUser} all trust + ''; + initialScript = pkgs.writeText "init.sql" '' + create user ${replicationUser} replication; + select * from pg_create_physical_replication_slot('${replicationSlot}'); + ''; }; - authentication = '' - host replication ${replicationUser} all trust - ''; - initialScript = pkgs.writeText "init.sql" '' - create user ${replicationUser} replication; - select * from pg_create_physical_replication_slot('${replicationSlot}'); - ''; + + services.postgresqlWalReceiver.receivers.main = { + postgresqlPackage = package; + connection = replicationConn; + slot = replicationSlot; + directory = walBackupDir; + }; + # This is only to speedup test, it isn't time racing. Service is set to autorestart always, + # default 60sec is fine for real system, but is too much for a test + systemd.services.postgresql-wal-receiver-main.serviceConfig.RestartSec = lib.mkForce 5; + systemd.services.postgresql.serviceConfig.ReadWritePaths = [ "/var/cache/wals" ]; }; - services.postgresqlWalReceiver.receivers.main = { - postgresqlPackage = package; - connection = replicationConn; - slot = replicationSlot; - directory = walBackupDir; - }; - # This is only to speedup test, it isn't time racing. Service is set to autorestart always, - # default 60sec is fine for real system, but is too much for a test - systemd.services.postgresql-wal-receiver-main.serviceConfig.RestartSec = lib.mkForce 5; - systemd.services.postgresql.serviceConfig.ReadWritePaths = [ "/var/cache/wals" ]; - }; + testScript = '' + # make an initial base backup + machine.wait_for_unit("postgresql.target") + machine.wait_for_unit("postgresql-wal-receiver-main") + # WAL receiver healthchecks PG every 5 seconds, so let's be sure they have connected each other + # required only for 9.4 + machine.sleep(5) + machine.succeed( + "${package}/bin/pg_basebackup --dbname=${replicationConn} --pgdata=${baseBackupDir}" + ) - testScript = '' - # make an initial base backup - machine.wait_for_unit("postgresql.target") - machine.wait_for_unit("postgresql-wal-receiver-main") - # WAL receiver healthchecks PG every 5 seconds, so let's be sure they have connected each other - # required only for 9.4 - machine.sleep(5) - machine.succeed( - "${package}/bin/pg_basebackup --dbname=${replicationConn} --pgdata=${baseBackupDir}" - ) + # create a dummy table with 100 records + machine.succeed( + "sudo -u postgres psql --command='create table dummy as select * from generate_series(1, 100) as val;'" + ) - # create a dummy table with 100 records - machine.succeed( - "sudo -u postgres psql --command='create table dummy as select * from generate_series(1, 100) as val;'" - ) + # stop postgres and destroy data + machine.systemctl("stop postgresql") + machine.systemctl("stop postgresql-wal-receiver-main") + machine.succeed("rm -r ${postgresqlDataDir}/{base,global,pg_*}") - # stop postgres and destroy data - machine.systemctl("stop postgresql") - machine.systemctl("stop postgresql-wal-receiver-main") - machine.succeed("rm -r ${postgresqlDataDir}/{base,global,pg_*}") + # restore the base backup + machine.succeed( + "cp -r ${baseBackupDir}/* ${postgresqlDataDir} && chown postgres:postgres -R ${postgresqlDataDir}" + ) - # restore the base backup - machine.succeed( - "cp -r ${baseBackupDir}/* ${postgresqlDataDir} && chown postgres:postgres -R ${postgresqlDataDir}" - ) + # prepare WAL and recovery + machine.succeed("chmod a+rX -R ${walBackupDir}") + machine.execute( + "for part in ${walBackupDir}/*.partial; do mv $part ''${part%%.*}; done" + ) # make use of partial segments too + machine.succeed( + "cp ${recoveryFile}/* ${postgresqlDataDir}/ && chmod 666 ${postgresqlDataDir}/recovery*" + ) - # prepare WAL and recovery - machine.succeed("chmod a+rX -R ${walBackupDir}") - machine.execute( - "for part in ${walBackupDir}/*.partial; do mv $part ''${part%%.*}; done" - ) # make use of partial segments too - machine.succeed( - "cp ${recoveryFile}/* ${postgresqlDataDir}/ && chmod 666 ${postgresqlDataDir}/recovery*" - ) + # replay WAL + machine.systemctl("start postgresql") + machine.wait_for_file("${postgresqlDataDir}/recovery.done") + machine.systemctl("restart postgresql") + machine.wait_for_unit("postgresql.target") - # replay WAL - machine.systemctl("start postgresql") - machine.wait_for_file("${postgresqlDataDir}/recovery.done") - machine.systemctl("restart postgresql") - machine.wait_for_unit("postgresql.target") - - # check that our records have been restored - machine.succeed( - "test $(sudo -u postgres psql --pset='pager=off' --tuples-only --command='select count(distinct val) from dummy;') -eq 100" - ) - ''; - }; + # check that our records have been restored + machine.succeed( + "test $(sudo -u postgres psql --pset='pager=off' --tuples-only --command='select count(distinct val) from dummy;') -eq 100" + ) + ''; + } + ); in genTests { inherit makeTestFor; } diff --git a/nixos/tests/postgresql/postgresql.nix b/nixos/tests/postgresql/postgresql.nix index 32a096346445..d832f5caa4e9 100644 --- a/nixos/tests/postgresql/postgresql.nix +++ b/nixos/tests/postgresql/postgresql.nix @@ -1,12 +1,11 @@ { - pkgs, - makeTest, + runTest, genTests, + lib, + ... }: let - inherit (pkgs) lib; - makeTestFor = package: lib.recurseIntoAttrs { @@ -15,263 +14,271 @@ let postgresql-clauses = makeEnsureTestFor package; }; - test-sql = pkgs.writeText "postgresql-test" '' - CREATE EXTENSION pgcrypto; -- just to check if lib loading works - CREATE TABLE sth ( - id int - ); - INSERT INTO sth (id) VALUES (1); - INSERT INTO sth (id) VALUES (1); - INSERT INTO sth (id) VALUES (1); - INSERT INTO sth (id) VALUES (1); - INSERT INTO sth (id) VALUES (1); - CREATE TABLE xmltest ( doc xml ); - INSERT INTO xmltest (doc) VALUES ('ok'); -- check if libxml2 enabled - - -- check if hardening gets relaxed - CREATE EXTENSION plv8; - -- try to trigger the V8 JIT, which requires MemoryDenyWriteExecute - DO $$ - let xs = []; - for (let i = 0, n = 400000; i < n; i++) { - xs.push(Math.round(Math.random() * n)) - } - console.log(xs.reduce((acc, x) => acc + x, 0)); - $$ LANGUAGE plv8; - ''; - makeTestForWithBackupAll = package: backupAll: - makeTest { - name = "postgresql${lib.optionalString backupAll "-backup-all"}-${package.name}"; - meta = with lib.maintainers; { - maintainers = [ zagy ]; - }; + runTest ( + { lib, pkgs, ... }: + let + test-sql = pkgs.writeText "postgresql-test" '' + CREATE EXTENSION pgcrypto; -- just to check if lib loading works + CREATE TABLE sth ( + id int + ); + INSERT INTO sth (id) VALUES (1); + INSERT INTO sth (id) VALUES (1); + INSERT INTO sth (id) VALUES (1); + INSERT INTO sth (id) VALUES (1); + INSERT INTO sth (id) VALUES (1); + CREATE TABLE xmltest ( doc xml ); + INSERT INTO xmltest (doc) VALUES ('ok'); -- check if libxml2 enabled - nodes.machine = - { config, ... }: - { - services.postgresql = { - inherit package; - enable = true; - identMap = '' - postgres root postgres - ''; - # TODO(@Ma27) split this off into its own VM test and move a few other - # extension tests to use postgresqlTestExtension. - extensions = ps: with ps; [ plv8 ]; - }; + -- check if hardening gets relaxed + CREATE EXTENSION plv8; + -- try to trigger the V8 JIT, which requires MemoryDenyWriteExecute + DO $$ + let xs = []; + for (let i = 0, n = 400000; i < n; i++) { + xs.push(Math.round(Math.random() * n)) + } + console.log(xs.reduce((acc, x) => acc + x, 0)); + $$ LANGUAGE plv8; + ''; - services.postgresqlBackup = { - enable = true; - databases = lib.optional (!backupAll) "postgres"; - pgdumpOptions = "--restrict-key=ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - pgdumpAllOptions = "--restrict-key=ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - }; + in + { + name = "postgresql${lib.optionalString backupAll "-backup-all"}-${package.name}"; + meta = with lib.maintainers; { + maintainers = [ zagy ]; }; - testScript = - let - backupName = if backupAll then "all" else "postgres"; - backupService = if backupAll then "postgresqlBackup" else "postgresqlBackup-postgres"; - backupFileBase = "/var/backup/postgresql/${backupName}"; - in - '' - def check_count(statement, lines): - return 'test $(psql -U postgres postgres -tAc "{}"|wc -l) -eq {}'.format( - statement, lines - ) + nodes.machine = + { config, ... }: + { + services.postgresql = { + inherit package; + enable = true; + identMap = '' + postgres root postgres + ''; + # TODO(@Ma27) split this off into its own VM test and move a few other + # extension tests to use postgresqlTestExtension. + extensions = ps: with ps; [ plv8 ]; + }; + + services.postgresqlBackup = { + enable = true; + databases = lib.optional (!backupAll) "postgres"; + pgdumpOptions = "--restrict-key=ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + pgdumpAllOptions = "--restrict-key=ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + }; + }; + + testScript = + let + backupName = if backupAll then "all" else "postgres"; + backupService = if backupAll then "postgresqlBackup" else "postgresqlBackup-postgres"; + backupFileBase = "/var/backup/postgresql/${backupName}"; + in + '' + def check_count(statement, lines): + return 'test $(psql -U postgres postgres -tAc "{}"|wc -l) -eq {}'.format( + statement, lines + ) - machine.start() - machine.wait_for_unit("postgresql.target") + machine.start() + machine.wait_for_unit("postgresql.target") - with subtest("Postgresql is available just after unit start"): - machine.succeed( - "cat ${test-sql} | sudo -u postgres psql" - ) + with subtest("Postgresql is available just after unit start"): + machine.succeed( + "cat ${test-sql} | sudo -u postgres psql" + ) - with subtest("Postgresql survives restart (bug #1735)"): - machine.shutdown() - import time - time.sleep(2) - machine.start() - machine.wait_for_unit("postgresql.target") + with subtest("Postgresql survives restart (bug #1735)"): + machine.shutdown() + import time + time.sleep(2) + machine.start() + machine.wait_for_unit("postgresql.target") - machine.fail(check_count("SELECT * FROM sth;", 3)) - machine.succeed(check_count("SELECT * FROM sth;", 5)) - machine.fail(check_count("SELECT * FROM sth;", 4)) - machine.succeed(check_count("SELECT xpath('/test/text()', doc) FROM xmltest;", 1)) + machine.fail(check_count("SELECT * FROM sth;", 3)) + machine.succeed(check_count("SELECT * FROM sth;", 5)) + machine.fail(check_count("SELECT * FROM sth;", 4)) + machine.succeed(check_count("SELECT xpath('/test/text()', doc) FROM xmltest;", 1)) - with subtest("killing postgres process should trigger an automatic restart"): - machine.succeed("systemctl kill -s KILL postgresql") + with subtest("killing postgres process should trigger an automatic restart"): + machine.succeed("systemctl kill -s KILL postgresql") - machine.wait_until_succeeds("systemctl is-active postgresql.service") - machine.wait_until_succeeds("systemctl is-active postgresql.target") + machine.wait_until_succeeds("systemctl is-active postgresql.service") + machine.wait_until_succeeds("systemctl is-active postgresql.target") - with subtest("Backup service works"): - machine.succeed( - "systemctl start ${backupService}.service", - "zcat ${backupFileBase}.sql.gz | grep 'ok'", - "ls -hal /var/backup/postgresql/ >/dev/console", - "stat -c '%a' ${backupFileBase}.sql.gz | grep 600", - ) - with subtest("Backup service removes prev files"): - machine.succeed( - # Create dummy prev files. - "touch ${backupFileBase}.prev.sql{,.gz,.zstd}", - "chown postgres:postgres ${backupFileBase}.prev.sql{,.gz,.zstd}", + with subtest("Backup service works"): + machine.succeed( + "systemctl start ${backupService}.service", + "zcat ${backupFileBase}.sql.gz | grep 'ok'", + "ls -hal /var/backup/postgresql/ >/dev/console", + "stat -c '%a' ${backupFileBase}.sql.gz | grep 600", + ) + with subtest("Backup service removes prev files"): + machine.succeed( + # Create dummy prev files. + "touch ${backupFileBase}.prev.sql{,.gz,.zstd}", + "chown postgres:postgres ${backupFileBase}.prev.sql{,.gz,.zstd}", - # Run backup. - "systemctl start ${backupService}.service", - "ls -hal /var/backup/postgresql/ >/dev/console", + # Run backup. + "systemctl start ${backupService}.service", + "ls -hal /var/backup/postgresql/ >/dev/console", - # Since nothing has changed in the database, the cur and prev files - # should match. - "zcat ${backupFileBase}.sql.gz | grep 'ok'", - "cmp ${backupFileBase}.sql.gz ${backupFileBase}.prev.sql.gz", + # Since nothing has changed in the database, the cur and prev files + # should match. + "zcat ${backupFileBase}.sql.gz | grep 'ok'", + "cmp ${backupFileBase}.sql.gz ${backupFileBase}.prev.sql.gz", - # The prev files with unused suffix should be removed. - "[ ! -f '${backupFileBase}.prev.sql' ]", - "[ ! -f '${backupFileBase}.prev.sql.zstd' ]", + # The prev files with unused suffix should be removed. + "[ ! -f '${backupFileBase}.prev.sql' ]", + "[ ! -f '${backupFileBase}.prev.sql.zstd' ]", - # Both cur and prev file should only be accessible by the postgres user. - "stat -c '%a' ${backupFileBase}.sql.gz | grep 600", - "stat -c '%a' '${backupFileBase}.prev.sql.gz' | grep 600", - ) - with subtest("Backup service fails gracefully"): - # Sabotage the backup process - machine.succeed("rm /run/postgresql/.s.PGSQL.5432") - machine.fail( - "systemctl start ${backupService}.service", - ) - machine.succeed( - "ls -hal /var/backup/postgresql/ >/dev/console", - "zcat ${backupFileBase}.prev.sql.gz | grep 'ok'", - "stat ${backupFileBase}.in-progress.sql.gz", - ) - # In a previous version, the second run would overwrite prev.sql.gz, - # so we test a second run as well. - machine.fail( - "systemctl start ${backupService}.service", - ) - machine.succeed( - "stat ${backupFileBase}.in-progress.sql.gz", - "zcat ${backupFileBase}.prev.sql.gz | grep 'ok'", - ) + # Both cur and prev file should only be accessible by the postgres user. + "stat -c '%a' ${backupFileBase}.sql.gz | grep 600", + "stat -c '%a' '${backupFileBase}.prev.sql.gz' | grep 600", + ) + with subtest("Backup service fails gracefully"): + # Sabotage the backup process + machine.succeed("rm /run/postgresql/.s.PGSQL.5432") + machine.fail( + "systemctl start ${backupService}.service", + ) + machine.succeed( + "ls -hal /var/backup/postgresql/ >/dev/console", + "zcat ${backupFileBase}.prev.sql.gz | grep 'ok'", + "stat ${backupFileBase}.in-progress.sql.gz", + ) + # In a previous version, the second run would overwrite prev.sql.gz, + # so we test a second run as well. + machine.fail( + "systemctl start ${backupService}.service", + ) + machine.succeed( + "stat ${backupFileBase}.in-progress.sql.gz", + "zcat ${backupFileBase}.prev.sql.gz | grep 'ok'", + ) - with subtest("Initdb works"): - machine.succeed("sudo -u postgres initdb -D /tmp/testpostgres2") + with subtest("Initdb works"): + machine.succeed("sudo -u postgres initdb -D /tmp/testpostgres2") - machine.log(machine.execute("systemd-analyze security postgresql.service | grep -v ✓")[1]) + machine.log(machine.execute("systemd-analyze security postgresql.service | grep -v ✓")[1]) - machine.shutdown() - ''; - }; + machine.shutdown() + ''; + } + ); makeEnsureTestFor = package: - makeTest { - name = "postgresql-clauses-${package.name}"; - meta = with lib.maintainers; { - maintainers = [ zagy ]; - }; - - nodes.machine = - { ... }: - { - services.postgresql = { - inherit package; - enable = true; - ensureUsers = [ - { - name = "all-clauses"; - ensureClauses = { - superuser = true; - createdb = true; - createrole = true; - "inherit" = true; - login = true; - replication = true; - bypassrls = true; - # SCRAM-SHA-256 hashed password for "password" - password = "SCRAM-SHA-256$4096:SZEJF5Si4QZ6l4fedrZZWQ==$6u3PWVcz+dts+NdpByPIjKa4CaSnoXGG3M2vpo76bVU=:WSZ0iGUCmVtKYVvNX0pFOp/60IgsdJ+90Y67Eun+QE0="; - connection_limit = 5; - }; - } - { - name = "default-clauses"; - } - ]; - }; + runTest ( + { lib, ... }: + { + name = "postgresql-clauses-${package.name}"; + meta = with lib.maintainers; { + maintainers = [ zagy ]; }; - testScript = - let - getClausesQuery = - user: - lib.concatStringsSep " " [ - "SELECT row_to_json(row)" - "FROM (" - "SELECT" - "rolsuper," - "rolinherit," - "rolcreaterole," - "rolcreatedb," - "rolcanlogin," - "rolreplication," - "rolbypassrls," - "rolconnlimit," - "rolpassword" - "FROM pg_authid" - "WHERE rolname = '${user}'" - ") row;" - ]; - in - '' - import json - machine.start() - machine.wait_for_unit("postgresql.target") + nodes.machine = + { ... }: + { + services.postgresql = { + inherit package; + enable = true; + ensureUsers = [ + { + name = "all-clauses"; + ensureClauses = { + superuser = true; + createdb = true; + createrole = true; + "inherit" = true; + login = true; + replication = true; + bypassrls = true; + # SCRAM-SHA-256 hashed password for "password" + password = "SCRAM-SHA-256$4096:SZEJF5Si4QZ6l4fedrZZWQ==$6u3PWVcz+dts+NdpByPIjKa4CaSnoXGG3M2vpo76bVU=:WSZ0iGUCmVtKYVvNX0pFOp/60IgsdJ+90Y67Eun+QE0="; + connection_limit = 5; + }; + } + { + name = "default-clauses"; + } + ]; + }; + }; - with subtest("All user permissions are set according to the ensureClauses attr"): - clauses = json.loads( - machine.succeed( - "sudo -u postgres psql -tc \"${getClausesQuery "all-clauses"}\"" + testScript = + let + getClausesQuery = + user: + lib.concatStringsSep " " [ + "SELECT row_to_json(row)" + "FROM (" + "SELECT" + "rolsuper," + "rolinherit," + "rolcreaterole," + "rolcreatedb," + "rolcanlogin," + "rolreplication," + "rolbypassrls," + "rolconnlimit," + "rolpassword" + "FROM pg_authid" + "WHERE rolname = '${user}'" + ") row;" + ]; + in + '' + import json + machine.start() + machine.wait_for_unit("postgresql.target") + + with subtest("All user permissions are set according to the ensureClauses attr"): + clauses = json.loads( + machine.succeed( + "sudo -u postgres psql -tc \"${getClausesQuery "all-clauses"}\"" + ) ) - ) - print(clauses) - t.assertTrue(clauses["rolsuper"]) - t.assertTrue(clauses["rolinherit"]) - t.assertTrue(clauses["rolcreaterole"]) - t.assertTrue(clauses["rolcreatedb"]) - t.assertTrue(clauses["rolcanlogin"]) - t.assertTrue(clauses["rolreplication"]) - t.assertTrue(clauses["rolbypassrls"]) - t.assertTrue(clauses["rolconnlimit"] == 5) - t.assertTrue(clauses["rolpassword"]) - machine.succeed( - "PGPASSWORD='password' psql -h localhost -U all-clauses -d postgres -c \"SELECT 1\"" - ) - - with subtest("All user permissions default when ensureClauses is not provided"): - clauses = json.loads( + print(clauses) + t.assertTrue(clauses["rolsuper"]) + t.assertTrue(clauses["rolinherit"]) + t.assertTrue(clauses["rolcreaterole"]) + t.assertTrue(clauses["rolcreatedb"]) + t.assertTrue(clauses["rolcanlogin"]) + t.assertTrue(clauses["rolreplication"]) + t.assertTrue(clauses["rolbypassrls"]) + t.assertTrue(clauses["rolconnlimit"] == 5) + t.assertTrue(clauses["rolpassword"]) machine.succeed( - "sudo -u postgres psql -tc \"${getClausesQuery "default-clauses"}\"" + "PGPASSWORD='password' psql -h localhost -U all-clauses -d postgres -c \"SELECT 1\"" ) - ) - t.assertFalse(clauses["rolsuper"]) - t.assertTrue(clauses["rolinherit"]) - t.assertFalse(clauses["rolcreaterole"]) - t.assertFalse(clauses["rolcreatedb"]) - t.assertTrue(clauses["rolcanlogin"]) - t.assertFalse(clauses["rolreplication"]) - t.assertFalse(clauses["rolbypassrls"]) - t.assertFalse(clauses["rolconnlimit"] == 5) - t.assertFalse(clauses["rolpassword"]) - machine.shutdown() - ''; - }; + with subtest("All user permissions default when ensureClauses is not provided"): + clauses = json.loads( + machine.succeed( + "sudo -u postgres psql -tc \"${getClausesQuery "default-clauses"}\"" + ) + ) + t.assertFalse(clauses["rolsuper"]) + t.assertTrue(clauses["rolinherit"]) + t.assertFalse(clauses["rolcreaterole"]) + t.assertFalse(clauses["rolcreatedb"]) + t.assertTrue(clauses["rolcanlogin"]) + t.assertFalse(clauses["rolreplication"]) + t.assertFalse(clauses["rolbypassrls"]) + t.assertFalse(clauses["rolconnlimit"] == 5) + t.assertFalse(clauses["rolpassword"]) + + machine.shutdown() + ''; + } + ); in genTests { inherit makeTestFor; } diff --git a/nixos/tests/postgresql/wal2json.nix b/nixos/tests/postgresql/wal2json.nix index 708c24ebabd6..511b1a7a2e8c 100644 --- a/nixos/tests/postgresql/wal2json.nix +++ b/nixos/tests/postgresql/wal2json.nix @@ -1,47 +1,48 @@ { - pkgs, - makeTest, + runTest, genTests, + ... }: let - inherit (pkgs) lib; - makeTestFor = package: - makeTest { - name = "wal2json-${package.name}"; - meta.maintainers = with pkgs.lib.maintainers; [ euank ]; + runTest ( + { lib, pkgs, ... }: + { + name = "wal2json-${package.name}"; + meta.maintainers = with pkgs.lib.maintainers; [ euank ]; - nodes.machine = { - services.postgresql = { - inherit package; - enable = true; - extensions = with package.pkgs; [ wal2json ]; - settings = { - wal_level = "logical"; - max_replication_slots = "10"; - max_wal_senders = "10"; + nodes.machine = { + services.postgresql = { + inherit package; + enable = true; + extensions = with package.pkgs; [ wal2json ]; + settings = { + wal_level = "logical"; + max_replication_slots = "10"; + max_wal_senders = "10"; + }; }; }; - }; - testScript = '' - machine.wait_for_unit("postgresql.target") - machine.succeed( - "sudo -u postgres psql -qAt -f ${./wal2json/example2.sql} postgres > /tmp/example2.out" - ) - machine.succeed( - "diff ${./wal2json/example2.out} /tmp/example2.out" - ) - machine.succeed( - "sudo -u postgres psql -qAt -f ${./wal2json/example3.sql} postgres > /tmp/example3.out" - ) - machine.succeed( - "diff ${./wal2json/example3.out} /tmp/example3.out" - ) - ''; - }; + testScript = '' + machine.wait_for_unit("postgresql.target") + machine.succeed( + "sudo -u postgres psql -qAt -f ${./wal2json/example2.sql} postgres > /tmp/example2.out" + ) + machine.succeed( + "diff ${./wal2json/example2.out} /tmp/example2.out" + ) + machine.succeed( + "sudo -u postgres psql -qAt -f ${./wal2json/example3.sql} postgres > /tmp/example3.out" + ) + machine.succeed( + "diff ${./wal2json/example3.out} /tmp/example3.out" + ) + ''; + } + ); in genTests { inherit makeTestFor;