diff --git a/checks/postgresql/default.nix b/checks/postgresql/default.nix index d334c03c..42baf9ca 100644 --- a/checks/postgresql/default.nix +++ b/checks/postgresql/default.nix @@ -9,14 +9,14 @@ self.clanModules.postgresql self.clanModules.localbackup ]; - clan.postgresl.databases = [ "test" ]; + clan.postgresl.databases.test = {}; clan.localbackup.targets.hdd.directory = "/mnt/external-disk"; }; testScript = '' start_all() machine.succeed("systemctl status postgresql") machine.wait_for_unit("postgresql") - machine.succeed("localbackup-create") + machine.succeed("/run/current-system/sw/bin/localbackup-create >&2") machine.succeed("ls -la /var/backups/postgresql") ''; }) diff --git a/clanModules/matrix-synapse/default.nix b/clanModules/matrix-synapse/default.nix index 09429a38..6a06708d 100644 --- a/clanModules/matrix-synapse/default.nix +++ b/clanModules/matrix-synapse/default.nix @@ -101,7 +101,6 @@ in ''; }; - services.postgresql.enable = true; services.nginx = { enable = true; virtualHosts = { diff --git a/clanModules/postgresql/default.nix b/clanModules/postgresql/default.nix index 5d819477..edb542af 100644 --- a/clanModules/postgresql/default.nix +++ b/clanModules/postgresql/default.nix @@ -8,7 +8,7 @@ let createDatatbaseState = db: let - folder = "/var/backup/postgresql/${db}"; + folder = "/var/backup/postgresql/${db.name}"; curFile = "${folder}/dump.sql.zstd"; prevFile = "${folder}/dump.sql.prev.zstd"; inProgressFile = "${folder}/dump.sql.in-progress.zstd"; @@ -21,7 +21,11 @@ let if [ -e ${curFile} ]; then mv ${curFile} ${prevFile} fi - pg_dump -C ${db} | \ + while [[ "$(systemctl is-active postgres)" == activating ]]; then + sleep 1 + done + systemctl is-active postgres + pg_dump -C ${db.name} | \ ${pkgs.zstd}/bin/zstd --rsyncable | \ > ${inProgressFile} mv ${inProgressFile} ${curFile} @@ -29,24 +33,93 @@ let ''; postRestoreCommand = '' if [[ -f ${prevFile} ]]; then - zstd --decompress --stdout ${prevFile} | psql -d ${db} + zstd --decompress --stdout ${prevFile} | psql -d ${db.name} fi ''; }; + + createDatabase = db: '' + CREATE DATABASE "${db.name}" ${ + lib.concatStringsSep " " ( + lib.mapAttrsToList (name: value: "${name} = ':${value}'") db.createOptions + ) + } + ''; + cfg = config.clan.postgresql; + + userClauses = lib.mapAttrsToList ( + _: user: "" + ''$PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc 'CREATE USER "${user.name}"' '' + ) cfg.users; + databaseClauses = lib.mapAttrsToList ( + name: db: + lib.optionalString (db.create) ''$PSQL -d postgres -c "SELECT 1 FROM pg_database WHERE datname = '${name}'" | grep -q 1 || $PSQL -d postgres -c ${lib.escapeShellArg (createDatabase db)} ${createDatabaseArgs db}'' + ) cfg.databases; in { options.clan.postgresl = { databases = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ "clan" ]; + default = { }; + type = lib.types.attrsOf ( + lib.types.submodule ( + { name, ... }: + { + options = { + name = lib.mkOption { + type = lib.types.str; + default = name; + }; + # set to false, in case the upstream module uses ensureDatabase option + create = lib.mkOption { + type = lib.types.bool; + default = true; + }; + createOptions = lib.mkOption { + type = lib.types.lazyAttrsOf lib.types.str; + default = { }; + example = { + TEMPLATE = "template0"; + LC_COLLATE = "C"; + LC_CTYPE = "C"; + ENCODING = "UTF8"; + OWNER = "foo"; + }; + }; + }; + } + ) + ); + }; + users = lib.mkOption { + default = { }; + type = lib.types.attrsOf ( + lib.types.submodule ( + { name, ... }: + { + options.name = lib.mkOption { + type = lib.types.str; + default = name; + }; + } + ) + ); }; }; config = { services.postgresql.enable = true; - clanCore.state = lib.listToAttrs ( - builtins.map ( - db: lib.nameValuePair "postgresql-${db}" (createDatatbaseState db) - ) config.clan.postgresl.databases - ); + # We are duplicating a bit the upstream module but allow to create databases with options + systemd.services.postgresql.postStart = '' + PSQL="psql --port=${builtins.toString config.services.postgresql.settings.port}" + + while ! $PSQL -d postgres -c "" 2> /dev/null; do + if ! kill -0 "$MAINPID"; then exit 1; fi + sleep 0.1 + done + ${lib.concatStringsSep "\n" userClauses} + ${lib.concatStringsSep "\n" databaseClauses} + ''; + clanCore.state = lib.mapAttrs' ( + _: db: lib.nameValuePair "postgresql-${db.name}" (createDatatbaseState db) + ) config.clan.postgresql.databases; }; }