1{ system ? builtins.currentSystem,
2 config ? {},
3 pkgs ? import ../.. { inherit system config; }
4}:
5
6with import ../lib/testing-python.nix { inherit system pkgs; };
7
8let
9 lib = pkgs.lib;
10
11 # Makes a test for a PostgreSQL package, given by name and looked up from `pkgs`.
12 makePostgresqlWalReceiverTest = postgresqlPackage:
13 {
14 name = postgresqlPackage;
15 value =
16 let
17 pkg = pkgs."${postgresqlPackage}";
18 postgresqlDataDir = "/var/lib/postgresql/${pkg.psqlSchema}";
19 replicationUser = "wal_receiver_user";
20 replicationSlot = "wal_receiver_slot";
21 replicationConn = "postgresql://${replicationUser}@localhost";
22 baseBackupDir = "/tmp/pg_basebackup";
23 walBackupDir = "/tmp/pg_wal";
24 atLeast12 = lib.versionAtLeast pkg.version "12.0";
25
26 recoveryFile = if atLeast12
27 then pkgs.writeTextDir "recovery.signal" ""
28 else pkgs.writeTextDir "recovery.conf" "restore_command = 'cp ${walBackupDir}/%f %p'";
29
30 in makeTest {
31 name = "postgresql-wal-receiver-${postgresqlPackage}";
32 meta.maintainers = with lib.maintainers; [ pacien ];
33
34 nodes.machine = { ... }: {
35 services.postgresql = {
36 package = pkg;
37 enable = true;
38 settings = lib.mkMerge [
39 {
40 wal_level = "archive"; # alias for replica on pg >= 9.6
41 max_wal_senders = 10;
42 max_replication_slots = 10;
43 }
44 (lib.mkIf atLeast12 {
45 restore_command = "cp ${walBackupDir}/%f %p";
46 recovery_end_command = "touch recovery.done";
47 })
48 ];
49 authentication = ''
50 host replication ${replicationUser} all trust
51 '';
52 initialScript = pkgs.writeText "init.sql" ''
53 create user ${replicationUser} replication;
54 select * from pg_create_physical_replication_slot('${replicationSlot}');
55 '';
56 };
57
58 services.postgresqlWalReceiver.receivers.main = {
59 postgresqlPackage = pkg;
60 connection = replicationConn;
61 slot = replicationSlot;
62 directory = walBackupDir;
63 };
64 # This is only to speedup test, it isn't time racing. Service is set to autorestart always,
65 # default 60sec is fine for real system, but is too much for a test
66 systemd.services.postgresql-wal-receiver-main.serviceConfig.RestartSec = lib.mkForce 5;
67 };
68
69 testScript = ''
70 # make an initial base backup
71 machine.wait_for_unit("postgresql")
72 machine.wait_for_unit("postgresql-wal-receiver-main")
73 # WAL receiver healthchecks PG every 5 seconds, so let's be sure they have connected each other
74 # required only for 9.4
75 machine.sleep(5)
76 machine.succeed(
77 "${pkg}/bin/pg_basebackup --dbname=${replicationConn} --pgdata=${baseBackupDir}"
78 )
79
80 # create a dummy table with 100 records
81 machine.succeed(
82 "sudo -u postgres psql --command='create table dummy as select * from generate_series(1, 100) as val;'"
83 )
84
85 # stop postgres and destroy data
86 machine.systemctl("stop postgresql")
87 machine.systemctl("stop postgresql-wal-receiver-main")
88 machine.succeed("rm -r ${postgresqlDataDir}/{base,global,pg_*}")
89
90 # restore the base backup
91 machine.succeed(
92 "cp -r ${baseBackupDir}/* ${postgresqlDataDir} && chown postgres:postgres -R ${postgresqlDataDir}"
93 )
94
95 # prepare WAL and recovery
96 machine.succeed("chmod a+rX -R ${walBackupDir}")
97 machine.execute(
98 "for part in ${walBackupDir}/*.partial; do mv $part ''${part%%.*}; done"
99 ) # make use of partial segments too
100 machine.succeed(
101 "cp ${recoveryFile}/* ${postgresqlDataDir}/ && chmod 666 ${postgresqlDataDir}/recovery*"
102 )
103
104 # replay WAL
105 machine.systemctl("start postgresql")
106 machine.wait_for_file("${postgresqlDataDir}/recovery.done")
107 machine.systemctl("restart postgresql")
108 machine.wait_for_unit("postgresql")
109
110 # check that our records have been restored
111 machine.succeed(
112 "test $(sudo -u postgres psql --pset='pager=off' --tuples-only --command='select count(distinct val) from dummy;') -eq 100"
113 )
114 '';
115 };
116 };
117
118# Maps the generic function over all attributes of PostgreSQL packages
119in builtins.listToAttrs (map makePostgresqlWalReceiverTest (builtins.attrNames (import ../../pkgs/servers/sql/postgresql pkgs)))