diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index 87b236dd5fd..5661edbee2d 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -105,6 +105,80 @@ in
'';
};
+ ensureDatabases = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ description = ''
+ Ensures that the specified databases exist.
+ This option will never delete existing databases, especially not when the value of this
+ option is changed. This means that databases created once through this option or
+ otherwise have to be removed manually.
+ '';
+ example = [
+ "gitea"
+ "nextcloud"
+ ];
+ };
+
+ ensureUsers = mkOption {
+ type = types.listOf (types.submodule {
+ options = {
+ name = mkOption {
+ type = types.str;
+ description = ''
+ Name of the user to ensure.
+ '';
+ };
+ ensurePermissions = mkOption {
+ type = types.attrsOf types.str;
+ default = {};
+ description = ''
+ Permissions to ensure for the user, specified as an attribute set.
+ The attribute names specify the database and tables to grant the permissions for.
+ The attribute values specify the permissions to grant. You may specify one or
+ multiple comma-separated SQL privileges here.
+
+ For more information on how to specify the target
+ and on which privileges exist, see the
+ GRANT syntax.
+ The attributes are used as GRANT ''${attrName} ON ''${attrValue}.
+ '';
+ example = literalExample ''
+ {
+ "DATABASE nextcloud" = "ALL PRIVILEGES";
+ "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
+ }
+ '';
+ };
+ };
+ });
+ default = [];
+ description = ''
+ Ensures that the specified users exist and have at least the ensured permissions.
+ The PostgreSQL users will be identified using peer authentication. This authenticates the Unix user with the
+ same name only, and that without the need for a password.
+ This option will never delete existing users or remove permissions, especially not when the value of this
+ option is changed. This means that users created and permissions assigned once through this option or
+ otherwise have to be removed manually.
+ '';
+ example = literalExample ''
+ [
+ {
+ name = "nextcloud";
+ ensurePermissions = {
+ "DATABASE nextcloud" = "ALL PRIVILEGES";
+ };
+ }
+ {
+ name = "superuser";
+ ensurePermissions = {
+ "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
+ };
+ }
+ ]
+ '';
+ };
+
enableTCPIP = mkOption {
type = types.bool;
default = false;
@@ -256,17 +330,30 @@ in
# Wait for PostgreSQL to be ready to accept connections.
postStart =
''
- while ! ${pkgs.sudo}/bin/sudo -u ${cfg.superUser} psql --port=${toString cfg.port} -d postgres -c "" 2> /dev/null; do
+ PSQL="${pkgs.sudo}/bin/sudo -u ${cfg.superUser} psql --port=${toString cfg.port}"
+
+ while ! $PSQL -d postgres -c "" 2> /dev/null; do
if ! kill -0 "$MAINPID"; then exit 1; fi
sleep 0.1
done
if test -e "${cfg.dataDir}/.first_startup"; then
${optionalString (cfg.initialScript != null) ''
- ${pkgs.sudo}/bin/sudo -u ${cfg.superUser} psql -f "${cfg.initialScript}" --port=${toString cfg.port} -d postgres
+ $PSQL -f "${cfg.initialScript}" -d postgres
''}
rm -f "${cfg.dataDir}/.first_startup"
fi
+ '' + optionalString (cfg.ensureDatabases != []) ''
+ ${concatMapStrings (database: ''
+ $PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${database}'" | grep -q 1 || $PSQL -tAc "CREATE DATABASE ${database}"
+ '') cfg.ensureDatabases}
+ '' + ''
+ ${concatMapStrings (user: ''
+ $PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc "CREATE USER ${user.name}"
+ ${concatStringsSep "\n" (mapAttrsToList (database: permission: ''
+ $PSQL -tAc "GRANT ${permission} ON ${database} TO ${user.name}"
+ '') user.ensurePermissions)}
+ '') cfg.ensureUsers}
'';
unitConfig.RequiresMountsFor = "${cfg.dataDir}";
diff --git a/nixos/tests/redmine.nix b/nixos/tests/redmine.nix
index ea72a0121d1..cbdb5c8d295 100644
--- a/nixos/tests/redmine.nix
+++ b/nixos/tests/redmine.nix
@@ -7,7 +7,7 @@ with import ../lib/testing.nix { inherit system pkgs; };
with pkgs.lib;
let
- redmineTest = package: makeTest {
+ mysqlTest = package: makeTest {
machine =
{ config, pkgs, ... }:
{ services.mysql.enable = true;
@@ -21,6 +21,7 @@ let
services.redmine.enable = true;
services.redmine.package = package;
+ services.redmine.database.type = "mysql2";
services.redmine.database.socket = "/run/mysqld/mysqld.sock";
services.redmine.plugins = {
redmine_env_auth = pkgs.fetchurl {
@@ -38,7 +39,44 @@ let
testScript = ''
startAll;
+ $machine->waitForUnit('redmine.service');
+ $machine->waitForOpenPort('3000');
+ $machine->succeed("curl --fail http://localhost:3000/");
+ '';
+ };
+ pgsqlTest = package: makeTest {
+ machine =
+ { config, pkgs, ... }:
+ { services.postgresql.enable = true;
+ services.postgresql.ensureDatabases = [ "redmine" ];
+ services.postgresql.ensureUsers = [
+ { name = "redmine";
+ ensurePermissions = { "DATABASE redmine" = "ALL PRIVILEGES"; };
+ }
+ ];
+
+ services.redmine.enable = true;
+ services.redmine.package = package;
+ services.redmine.database.type = "postgresql";
+ services.redmine.database.host = "";
+ services.redmine.database.port = 5432;
+ services.redmine.plugins = {
+ redmine_env_auth = pkgs.fetchurl {
+ url = https://github.com/Intera/redmine_env_auth/archive/0.7.zip;
+ sha256 = "1xb8lyarc7mpi86yflnlgyllh9hfwb9z304f19dx409gqpia99sc";
+ };
+ };
+ services.redmine.themes = {
+ dkuk-redmine_alex_skin = pkgs.fetchurl {
+ url = https://bitbucket.org/dkuk/redmine_alex_skin/get/1842ef675ef3.zip;
+ sha256 = "0hrin9lzyi50k4w2bd2b30vrf1i4fi1c0gyas5801wn8i7kpm9yl";
+ };
+ };
+ };
+
+ testScript = ''
+ startAll;
$machine->waitForUnit('redmine.service');
$machine->waitForOpenPort('3000');
$machine->succeed("curl --fail http://localhost:3000/");
@@ -46,13 +84,18 @@ let
};
in
{
- redmine_3 = redmineTest pkgs.redmine // {
- name = "redmine_3";
+ v3-mysql = mysqlTest pkgs.redmine // {
+ name = "v3-mysql";
meta.maintainers = [ maintainers.aanderse ];
};
- redmine_4 = redmineTest pkgs.redmine_4 // {
- name = "redmine_4";
+ v4-mysql = mysqlTest pkgs.redmine_4 // {
+ name = "v4-mysql";
+ meta.maintainers = [ maintainers.aanderse ];
+ };
+
+ v4-pgsql = pgsqlTest pkgs.redmine_4 // {
+ name = "v4-pgsql";
meta.maintainers = [ maintainers.aanderse ];
};
}