diff --git a/checks/postgresql/default.nix b/checks/postgresql/default.nix index 2a0eeadc..91bf2e52 100644 --- a/checks/postgresql/default.nix +++ b/checks/postgresql/default.nix @@ -11,8 +11,19 @@ ]; clan.postgresql.users.test = { }; clan.postgresql.databases.test.create.options.OWNER = "test"; + clan.postgresql.databases.test.restore.stopOnRestore = [ "sample-service" ]; clan.localbackup.targets.hdd.directory = "/mnt/external-disk"; + systemd.services.sample-service = { + wantedBy = [ "multi-user.target" ]; + script = '' + while true; do + echo "Hello, world!" + sleep 5 + done + ''; + }; + environment.systemPackages = [ config.services.postgresql.package ]; }; testScript = @@ -20,20 +31,22 @@ '' start_all() machine.wait_for_unit("postgresql") + machine.wait_for_unit("sample-service") # Create a test table machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -c 'CREATE TABLE test (id serial PRIMARY KEY);' test") machine.succeed("/run/current-system/sw/bin/localbackup-create >&2") + timestamp_before = int(machine.succeed("systemctl show --property=ExecMainStartTimestampMonotonic sample-service | cut -d= -f2").strip()) - machine.succeed("test -e /mnt/external-disk/snapshot.0/machine/var/backup/postgres/test/dump.sql.zstd || { echo 'dump.sql.zstd not found'; exit 1; }") + machine.succeed("test -e /mnt/external-disk/snapshot.0/machine/var/backup/postgres/test/pg-dump || { echo 'pg-dump not found'; exit 1; }") machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -d test -c 'INSERT INTO test DEFAULT VALUES;'") machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -d test -c 'DROP TABLE test;'") - machine.succeed("test -e /var/backup/postgres/test/dump.sql.zstd || { echo 'backup.info not found'; exit 1; }") + machine.succeed("test -e /var/backup/postgres/test/pg-dump || { echo 'pg-dump not found'; exit 1; }") machine.succeed("rm -rf /var/backup/postgres") machine.succeed("NAME=/mnt/external-disk/snapshot.0 FOLDERS=/var/backup/postgres/test /run/current-system/sw/bin/localbackup-restore >&2") - machine.succeed("test -e /var/backup/postgres/test/dump.sql.zstd || { echo 'backup.info not found'; exit 1; }") + machine.succeed("test -e /var/backup/postgres/test/pg-dump || { echo 'pg-dump not found'; exit 1; }") machine.succeed(""" set -x @@ -42,6 +55,9 @@ machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -l >&2") machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -d test -c '\dt' >&2") + timestamp_after = int(machine.succeed("systemctl show --property=ExecMainStartTimestampMonotonic sample-service | cut -d= -f2").strip()) + assert timestamp_before < timestamp_after, f"{timestamp_before} >= {timestamp_after}: expected sample-service to be restarted after restore" + # Check that the table is still there machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -d test -c 'SELECT * FROM test;'") output = machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql --csv -c \"SELECT datdba::regrole FROM pg_database WHERE datname = 'test'\"") diff --git a/clanModules/postgresql/default.nix b/clanModules/postgresql/default.nix index c1b24104..b1e7621c 100644 --- a/clanModules/postgresql/default.nix +++ b/clanModules/postgresql/default.nix @@ -9,7 +9,8 @@ let db: let folder = "/var/backup/postgres/${db.name}"; - current = "${folder}/dump.sql.zstd"; + current = "${folder}/pg-dump"; + compression = lib.optionalString (lib.versionAtLeast config.services.postgresql.package.version "16") "--compress=zstd"; in { folders = [ folder ]; @@ -28,10 +29,9 @@ let done mkdir -p "${folder}" - runuser -u postgres -- pg_dump --compress=zstd --dbname=${db.name} -Fc -c > "${current}.tmp" + runuser -u postgres -- pg_dump ${compression} --dbname=${db.name} -Fc -c > "${current}.tmp" mv "${current}.tmp" ${current} ''; - postRestoreCommand = '' export PATH=${ lib.makeBinPath [ @@ -52,10 +52,14 @@ let done if [[ -e "${current}" ]]; then - umask 077 # only root can read the backup - mkdir -p "${folder}" - runuser -u postgres -- dropdb "${db.name}" - runuser -u postgres -- pg_restore -C -d postgres "${current}" + ( + systemctl stop ${lib.concatStringsSep " " db.restore.stopOnRestore} + trap "systemctl start ${lib.concatStringsSep " " db.restore.stopOnRestore}" EXIT + + mkdir -p "${folder}" + runuser -u postgres -- dropdb "${db.name}" + runuser -u postgres -- pg_restore -C -d postgres "${current}" + ) else echo No database backup found, skipping restore fi @@ -111,10 +115,10 @@ in OWNER = "foo"; }; }; - backup.enable = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Backup the database."; + restore.stopOnRestore = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = "List of services to stop before restoring the database."; }; }; } diff --git a/nixosModules/clanCore/state.nix b/nixosModules/clanCore/state.nix index 24054cbc..1128fdcc 100644 --- a/nixosModules/clanCore/state.nix +++ b/nixosModules/clanCore/state.nix @@ -33,6 +33,18 @@ e.g. a database dump ''; }; + + # TODO: implement this + #stopOnRestore = lib.mkOption { + # type = lib.types.listOf lib.types.str; + # default = []; + # description = '' + # List of services to stop before restoring the state dir from a backup + + # Utilize this to stop services which currently access these folders or or other services affected by the restore + # ''; + #}; + preRestoreCommand = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; @@ -42,6 +54,7 @@ Utilize this to stop services which currently access these folders ''; }; + postRestoreCommand = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null;