at master 4.2 kB view raw
1{ lib, pkgs, ... }: 2let 3 inherit (import ../ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey; 4 backupPath = "/var/lib/pgbackrest"; 5in 6{ 7 name = "pgbackrest-posix"; 8 9 meta = { 10 maintainers = with lib.maintainers; [ wolfgangwalther ]; 11 }; 12 13 nodes.primary = 14 { 15 pkgs, 16 ... 17 }: 18 { 19 services.openssh.enable = true; 20 users.users.postgres.openssh.authorizedKeys.keys = [ 21 snakeOilPublicKey 22 ]; 23 24 services.postgresql = { 25 enable = true; 26 initialScript = pkgs.writeText "init.sql" '' 27 CREATE TABLE t(c text); 28 INSERT INTO t VALUES ('hello world'); 29 ''; 30 # To make sure we're waiting for read-write after recovery. 31 ensureUsers = [ 32 { 33 name = "app-user"; 34 ensureClauses.login = true; 35 } 36 ]; 37 }; 38 39 services.pgbackrest = { 40 enable = true; 41 repos.backup = { 42 type = "posix"; 43 path = backupPath; 44 host-user = "pgbackrest"; 45 }; 46 }; 47 }; 48 49 nodes.backup = 50 { 51 nodes, 52 ... 53 }: 54 { 55 services.openssh.enable = true; 56 users.users.pgbackrest.openssh.authorizedKeys.keys = [ 57 snakeOilPublicKey 58 ]; 59 60 services.pgbackrest = { 61 enable = true; 62 repos.localhost.path = backupPath; 63 64 stanzas.default = { 65 jobs.future = { 66 schedule = "3000-01-01"; 67 type = "full"; 68 }; 69 instances.primary = { 70 path = nodes.primary.services.postgresql.dataDir; 71 user = "postgres"; 72 }; 73 }; 74 75 # Examples from https://pgbackrest.org/configuration.html#introduction 76 # Not used for the test, except for dumping the config. 77 stanzas.config-format.settings = { 78 start-fast = true; 79 compress-level = 3; 80 buffer-size = "2MiB"; 81 db-timeout = 600; 82 db-exclude = [ 83 "db1" 84 "db2" 85 "db5" 86 ]; 87 tablespace-map = { 88 ts_01 = "/db/ts_01"; 89 ts_02 = "/db/ts_02"; 90 }; 91 }; 92 }; 93 }; 94 95 testScript = 96 { nodes, ... }: 97 '' 98 start_all() 99 100 primary.wait_for_unit("multi-user.target") 101 backup.wait_for_unit("multi-user.target") 102 103 with subtest("config file is written correctly"): 104 from textwrap import dedent 105 have = backup.succeed("cat /etc/pgbackrest/pgbackrest.conf") 106 want = dedent("""\ 107 [config-format] 108 buffer-size=2MiB 109 compress-level=3 110 db-exclude=db1 111 db-exclude=db2 112 db-exclude=db5 113 db-timeout=600 114 start-fast=y 115 tablespace-map=ts_01=/db/ts_01 116 tablespace-map=ts_02=/db/ts_02 117 """) 118 assert want in have, repr((want, have)) 119 120 primary.log(primary.succeed(""" 121 HOME="${nodes.primary.services.postgresql.dataDir}" 122 mkdir -m 700 -p ~/.ssh 123 cat ${snakeOilPrivateKey} > ~/.ssh/id_ecdsa 124 chmod 400 ~/.ssh/id_ecdsa 125 ssh-keyscan backup >> ~/.ssh/known_hosts 126 chown -R postgres:postgres ~/.ssh 127 """)) 128 129 backup.log(backup.succeed(""" 130 HOME="${backupPath}" 131 mkdir -m 700 -p ~/.ssh 132 cat ${snakeOilPrivateKey} > ~/.ssh/id_ecdsa 133 chmod 400 ~/.ssh/id_ecdsa 134 ssh-keyscan primary >> ~/.ssh/known_hosts 135 chown -R pgbackrest:pgbackrest ~ 136 """)) 137 138 with subtest("backup/restore works with remote instance/local repo (SSH)"): 139 backup.succeed("sudo -u pgbackrest pgbackrest --stanza=default stanza-create") 140 backup.succeed("sudo -u pgbackrest pgbackrest --stanza=default check") 141 142 backup.systemctl("start pgbackrest-default-future") 143 144 # corrupt cluster 145 primary.systemctl("stop postgresql") 146 primary.execute("rm ${nodes.primary.services.postgresql.dataDir}/global/pg_control") 147 148 primary.succeed("sudo -u postgres pgbackrest --stanza=default restore --delta") 149 150 primary.systemctl("start postgresql") 151 primary.wait_for_unit("postgresql.target") 152 assert "hello world" in primary.succeed("sudo -u postgres psql -c 'TABLE t;'") 153 ''; 154}