nixos/postgresql: add ensureDatabases & ensureUsers options (#56720)

nixos/postgresql: add ensureDatabases & ensureUsers options
This commit is contained in:
Florian Klink 2019-05-20 10:58:48 +02:00 committed by GitHub
commit cd96b50d90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 137 additions and 7 deletions

View File

@ -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
<link xlink:href="https://www.postgresql.org/docs/current/sql-grant.html">GRANT syntax</link>.
The attributes are used as <code>GRANT ''${attrName} ON ''${attrValue}</code>.
'';
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}";

View File

@ -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 ];
};
}