diff options
Diffstat (limited to 'nixos/modules/services/databases')
-rw-r--r-- | nixos/modules/services/databases/4store-endpoint.nix | 72 | ||||
-rw-r--r-- | nixos/modules/services/databases/4store.nix | 71 | ||||
-rw-r--r-- | nixos/modules/services/databases/firebird.nix | 149 | ||||
-rw-r--r-- | nixos/modules/services/databases/memcached.nix | 97 | ||||
-rw-r--r-- | nixos/modules/services/databases/mongodb.nix | 130 | ||||
-rw-r--r-- | nixos/modules/services/databases/mysql.nix | 245 | ||||
-rw-r--r-- | nixos/modules/services/databases/mysql55.nix | 248 | ||||
-rw-r--r-- | nixos/modules/services/databases/openldap.nix | 58 | ||||
-rw-r--r-- | nixos/modules/services/databases/postgresql.nix | 232 | ||||
-rw-r--r-- | nixos/modules/services/databases/redis.nix | 216 | ||||
-rw-r--r-- | nixos/modules/services/databases/virtuoso.nix | 98 |
11 files changed, 1616 insertions, 0 deletions
diff --git a/nixos/modules/services/databases/4store-endpoint.nix b/nixos/modules/services/databases/4store-endpoint.nix new file mode 100644 index 00000000000..7b03b4d8f1d --- /dev/null +++ b/nixos/modules/services/databases/4store-endpoint.nix @@ -0,0 +1,72 @@ +{ config, pkgs, ... }: +let + cfg = config.services.fourStoreEndpoint; + endpointUser = "fourstorehttp"; + run = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${endpointUser} -c"; +in +with pkgs.lib; +{ + + ###### interface + + options = { + + services.fourStoreEndpoint = { + + enable = mkOption { + default = false; + description = "Whether to enable 4Store SPARQL endpoint."; + }; + + database = mkOption { + default = config.services.fourStore.database; + description = "RDF database name to expose via the endpoint. Defaults to local 4Store database name."; + }; + + listenAddress = mkOption { + default = null; + description = "IP address to listen on."; + }; + + port = mkOption { + default = 8080; + description = "port to listen on."; + }; + + options = mkOption { + default = ""; + description = "Extra CLI options to pass to 4Store's 4s-httpd process."; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable ( + mkAssert (cfg.enable -> cfg.database != "") + "Must specify database name" { + + users.extraUsers = singleton + { name = endpointUser; + uid = config.ids.uids.fourStoreEndpoint; + description = "4Store SPARQL endpoint user"; +# home = stateDir; + }; + + services.avahi.enable = true; + + jobs.fourStoreEndpoint = { + name = "4store-endpoint"; + startOn = "filesystem"; + + exec = '' + ${run} '${pkgs.rdf4store}/bin/4s-httpd -D ${cfg.options} ${if cfg.listenAddress!=null then "-H ${cfg.listenAddress}" else "" } -p ${toString cfg.port} ${cfg.database}' + ''; + }; + + }); + +} diff --git a/nixos/modules/services/databases/4store.nix b/nixos/modules/services/databases/4store.nix new file mode 100644 index 00000000000..14990e92ea3 --- /dev/null +++ b/nixos/modules/services/databases/4store.nix @@ -0,0 +1,71 @@ +{ config, pkgs, ... }: +let + cfg = config.services.fourStore; + stateDir = "/var/lib/4store"; + fourStoreUser = "fourstore"; + run = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${fourStoreUser}"; +in +with pkgs.lib; +{ + + ###### interface + + options = { + + services.fourStore = { + + enable = mkOption { + default = false; + description = "Whether to enable 4Store RDF database server."; + }; + + database = mkOption { + default = ""; + description = "RDF database name. If it doesn't exist, it will be created. Databases are stored in ${stateDir}."; + }; + + options = mkOption { + default = ""; + description = "Extra CLI options to pass to 4Store."; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable ( + mkAssert (cfg.enable -> cfg.database != "") + "Must specify database name" { + + users.extraUsers = singleton + { name = fourStoreUser; + uid = config.ids.uids.fourStore; + description = "4Store database user"; + home = stateDir; + }; + + services.avahi.enable = true; + + jobs.fourStore = { + name = "4store"; + startOn = "filesystem"; + + preStart = '' + mkdir -p ${stateDir}/ + chown ${fourStoreUser} ${stateDir} + if ! test -e "${stateDir}/${cfg.database}"; then + ${run} -c '${pkgs.rdf4store}/bin/4s-backend-setup ${cfg.database}' + fi + ''; + + exec = '' + ${run} -c '${pkgs.rdf4store}/bin/4s-backend -D ${cfg.options} ${cfg.database}' + ''; + }; + + }); + +} diff --git a/nixos/modules/services/databases/firebird.nix b/nixos/modules/services/databases/firebird.nix new file mode 100644 index 00000000000..aca0d58900b --- /dev/null +++ b/nixos/modules/services/databases/firebird.nix @@ -0,0 +1,149 @@ +{ config, pkgs, ... }: + +# TODO: this file needs some additional work - at least you can connect to +# firebird .. +# Example how to connect: +# isql /var/db/firebird/data/your-db.fdb -u sysdba -p <default password> + +# There are at least two ways to run firebird. superserver has been choosen +# however there are no strong reasons to prefer this or the other one AFAIK +# Eg superserver is said to be most efficiently using resources according to +# http://www.firebirdsql.org/manual/qsg25-classic-or-super.html + +with pkgs.lib; + +let + + cfg = config.services.firebird; + + firebird = cfg.package; + + pidFile = "${cfg.pidDir}/firebirdd.pid"; + +in + +{ + + ###### interface + + options = { + + services.firebird = { + + enable = mkOption { + default = false; + description = " + Whether to enable the firebird super server. + "; + }; + + package = mkOption { + default = pkgs.firebirdSuper; + /* + Example: <code>package = pkgs.firebirdSuper.override { icu = + pkgs.icu; };</code> which is not recommended for compatibility + reasons. See comments at the firebirdSuper derivation + */ + + description = " + Which firebird derivation to use. + "; + }; + + port = mkOption { + default = "3050"; + description = "Port of Firebird."; + }; + + user = mkOption { + default = "firebird"; + description = "User account under which firebird runs."; + }; + + dataDir = mkOption { + default = "/var/db/firebird/data"; # ubuntu is using /var/lib/firebird/2.1/data/.. ? + description = "Location where firebird databases are stored."; + }; + + pidDir = mkOption { + default = "/run/firebird"; + description = "Location of the file which stores the PID of the firebird server."; + }; + + }; + + }; + + + ###### implementation + + config = mkIf config.services.firebird.enable { + + users.extraUsers.firebird.description = "Firebird server user"; + + environment.systemPackages = [firebird]; + + systemd.services.firebird = + { description = "firebird super server"; + + wantedBy = [ "multi-user.target" ]; + + # TODO: moving security2.fdb into the data directory works, maybe there + # is a better way + preStart = + '' + secureDir="${cfg.dataDir}/../system" + + mkdir -m 0700 -p \ + "${cfg.dataDir}" \ + "${cfg.pidDir}" \ + /var/log/firebird \ + "$secureDir" + + if ! test -e "$secureDir/security2.fdb"; then + cp ${firebird}/security2.fdb "$secureDir" + fi + + chown -R ${cfg.user} "${cfg.pidDir}" "${cfg.dataDir}" "$secureDir" /var/log/firebird + chmod -R 700 "${cfg.pidDir}" "${cfg.dataDir}" "$secureDir" /var/log/firebird + ''; + + serviceConfig.PermissionsStartOnly = true; # preStart must be run as root + serviceConfig.User = cfg.user; + serviceConfig.ExecStart = ''${firebird}/bin/fbserver -d''; + + # TODO think about shutdown + }; + + environment.etc."firebird/firebird.msg".source = "${firebird}/firebird.msg"; + + # think about this again - and eventually make it an option + environment.etc."firebird/firebird.conf".text = '' + # RootDirectory = Restrict ${cfg.dataDir} + DatabaseAccess = Restrict ${cfg.dataDir} + ExternalFileAccess = Restrict ${cfg.dataDir} + # what is this? is None allowed? + UdfAccess = None + # "Native" = traditional interbase/firebird, "mixed" is windows only + Authentication = Native + + # defaults to -1 on non Win32 + #MaxUnflushedWrites = 100 + #MaxUnflushedWriteTime = 100 + + # show trace if trouble occurs (does this require debug build?) + # BugcheckAbort = 0 + # ConnectionTimeout = 180 + + #RemoteServiceName = gds_db + RemoteServicePort = ${cfg.port} + + # randomly choose port for server Event Notification + #RemoteAuxPort = 0 + # rsetrict connections to a network card: + #RemoteBindAddress = + # there are some more settings .. + ''; + }; + +} diff --git a/nixos/modules/services/databases/memcached.nix b/nixos/modules/services/databases/memcached.nix new file mode 100644 index 00000000000..a0e264f2299 --- /dev/null +++ b/nixos/modules/services/databases/memcached.nix @@ -0,0 +1,97 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + + cfg = config.services.memcached; + + memcached = pkgs.memcached; + +in + +{ + + ###### interface + + options = { + + services.memcached = { + + enable = mkOption { + default = false; + description = " + Whether to enable Memcached. + "; + }; + + user = mkOption { + default = "memcached"; + description = "The user to run Memcached as"; + }; + + listen = mkOption { + default = "127.0.0.1"; + description = "The IP address to bind to"; + }; + + port = mkOption { + default = 11211; + description = "The port to bind to"; + }; + + socket = mkOption { + default = ""; + description = "Unix socket path to listen on. Setting this will disable network support"; + example = "/var/run/memcached"; + }; + + maxMemory = mkOption { + default = 64; + description = "The maximum amount of memory to use for storage, in megabytes."; + }; + + maxConnections = mkOption { + default = 1024; + description = "The maximum number of simultaneous connections"; + }; + + extraOptions = mkOption { + default = []; + description = "A list of extra options that will be added as a suffix when running memcached"; + }; + }; + + }; + + ###### implementation + + config = mkIf config.services.memcached.enable { + + users.extraUsers = singleton + { name = cfg.user; + description = "Memcached server user"; + }; + + environment.systemPackages = [ memcached ]; + + systemd.services.memcached = + { description = "Memcached server"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + ExecStart = + let + networking = if cfg.socket != "" + then "-s ${cfg.socket}" + else "-l ${cfg.listen} -p ${toString cfg.port}"; + in "${memcached}/bin/memcached ${networking} -m ${toString cfg.maxMemory} -c ${toString cfg.maxConnections} ${concatStringsSep " " cfg.extraOptions}"; + + User = cfg.user; + }; + }; + }; + +} diff --git a/nixos/modules/services/databases/mongodb.nix b/nixos/modules/services/databases/mongodb.nix new file mode 100644 index 00000000000..d6299415893 --- /dev/null +++ b/nixos/modules/services/databases/mongodb.nix @@ -0,0 +1,130 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + + b2s = x: if x then "true" else "false"; + + cfg = config.services.mongodb; + + mongodb = cfg.package; + + mongoCnf = pkgs.writeText "mongodb.conf" + '' + bind_ip = ${cfg.bind_ip} + ${optionalString cfg.quiet "quiet = true"} + dbpath = ${cfg.dbpath} + logpath = ${cfg.logpath} + logappend = ${b2s cfg.logappend} + ${optionalString (cfg.replSetName != "") "replSet = ${cfg.replSetName}"} + ''; + +in + +{ + + ###### interface + + options = { + + services.mongodb = { + + enable = mkOption { + default = false; + description = " + Whether to enable the MongoDB server. + "; + }; + + package = mkOption { + default = pkgs.mongodb; + description = " + Which MongoDB derivation to use. + "; + }; + + user = mkOption { + default = "mongodb"; + description = "User account under which MongoDB runs"; + }; + + bind_ip = mkOption { + default = "127.0.0.1"; + description = "IP to bind to"; + }; + + quiet = mkOption { + default = false; + description = "quieter output"; + }; + + dbpath = mkOption { + default = "/var/db/mongodb"; + description = "Location where MongoDB stores its files"; + }; + + logpath = mkOption { + default = "/var/log/mongodb/mongod.log"; + description = "Location where MongoDB stores its logfile"; + }; + + logappend = mkOption { + default = true; + description = "Append logfile instead over overwriting"; + }; + + replSetName = mkOption { + default = ""; + description = '' + If this instance is part of a replica set, set its name here. + Otherwise, leave empty to run as single node. + ''; + }; + }; + + }; + + + ###### implementation + + config = mkIf config.services.mongodb.enable { + + users.extraUsers = singleton + { name = cfg.user; + description = "MongoDB server user"; + }; + + environment.systemPackages = [ mongodb ]; + + systemd.services.mongodb_init = + { description = "MongoDB server initialisation"; + + wantedBy = [ "mongodb.service" ]; + before = [ "mongodb.service" ]; + + serviceConfig.Type = "oneshot"; + + script = '' + if ! test -e ${cfg.dbpath}; then + install -d -m0700 -o ${cfg.user} ${cfg.dbpath} + install -d -m0755 -o ${cfg.user} `dirname ${cfg.logpath}` + fi + ''; + }; + + systemd.services.mongodb = + { description = "MongoDB server"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + ExecStart = "${mongodb}/bin/mongod --quiet --config ${mongoCnf}"; + User = cfg.user; + }; + }; + + }; + +} diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix new file mode 100644 index 00000000000..663c2cc4505 --- /dev/null +++ b/nixos/modules/services/databases/mysql.nix @@ -0,0 +1,245 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + + cfg = config.services.mysql; + + mysql = cfg.package; + + pidFile = "${cfg.pidDir}/mysqld.pid"; + + mysqldOptions = + "--user=${cfg.user} --datadir=${cfg.dataDir} " + + "--pid-file=${pidFile}"; + + myCnf = pkgs.writeText "my.cnf" + '' + [mysqld] + ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "log-bin=mysql-bin"} + ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "server-id = ${toString cfg.replication.serverId}"} + ${optionalString (cfg.replication.role == "slave") + '' + master-host = ${cfg.replication.masterHost} + master-user = ${cfg.replication.masterUser} + master-password = ${cfg.replication.masterPassword} + master-port = ${toString cfg.replication.masterPort} + ''} + ${cfg.extraOptions} + ''; + +in + +{ + + ###### interface + + options = { + + services.mysql = { + + enable = mkOption { + default = false; + description = " + Whether to enable the MySQL server. + "; + }; + + package = mkOption { + default = pkgs.mysql; + description = " + Which MySQL derivation to use. + "; + }; + + port = mkOption { + default = "3306"; + description = "Port of MySQL"; + }; + + user = mkOption { + default = "mysql"; + description = "User account under which MySQL runs"; + }; + + dataDir = mkOption { + default = "/var/mysql"; # !!! should be /var/db/mysql + description = "Location where MySQL stores its table files"; + }; + + pidDir = mkOption { + default = "/var/run/mysql"; + description = "Location of the file which stores the PID of the MySQL server"; + }; + + extraOptions = mkOption { + default = ""; + example = '' + key_buffer_size = 6G + table_cache = 1600 + log-error = /var/log/mysql_err.log + ''; + description = '' + Provide extra options to the MySQL configuration file. + + Please note, that these options are added to the + <literal>[mysqld]</literal> section so you don't need to explicitly + state it again. + ''; + }; + + initialDatabases = mkOption { + default = []; + description = "List of database names and their initial schemas that should be used to create databases on the first startup of MySQL"; + example = [ + { name = "foodatabase"; schema = ./foodatabase.sql; } + { name = "bardatabase"; schema = ./bardatabase.sql; } + ]; + }; + + initialScript = mkOption { + default = null; + description = "A file containing SQL statements to be executed on the first startup. Can be used for granting certain permissions on the database"; + }; + + # FIXME: remove this option; it's a really bad idea. + rootPassword = mkOption { + default = null; + description = "Path to a file containing the root password, modified on the first startup. Not specifying a root password will leave the root password empty."; + }; + + replication = { + role = mkOption { + default = "none"; + description = "Role of the MySQL server instance. Can be either: master, slave or none"; + }; + + serverId = mkOption { + default = 1; + description = "Id of the MySQL server instance. This number must be unique for each instance"; + }; + + masterHost = mkOption { + description = "Hostname of the MySQL master server"; + }; + + masterUser = mkOption { + description = "Username of the MySQL replication user"; + }; + + masterPassword = mkOption { + description = "Password of the MySQL replication user"; + }; + + masterPort = mkOption { + default = 3306; + description = "Port number on which the MySQL master server runs"; + }; + }; + }; + + }; + + + ###### implementation + + config = mkIf config.services.mysql.enable { + + users.extraUsers.mysql = { + description = "MySQL server user"; + group = "mysql"; + uid = config.ids.uids.mysql; + }; + + users.extraGroups.mysql.gid = config.ids.gids.mysql; + + environment.systemPackages = [mysql]; + + systemd.services.mysql = + { description = "MySQL Server"; + + wantedBy = [ "multi-user.target" ]; + + unitConfig.RequiresMountsFor = "${cfg.dataDir}"; + + preStart = + '' + if ! test -e ${cfg.dataDir}/mysql; then + mkdir -m 0700 -p ${cfg.dataDir} + chown -R ${cfg.user} ${cfg.dataDir} + ${mysql}/bin/mysql_install_db ${mysqldOptions} + touch /tmp/mysql_init + fi + + mkdir -m 0700 -p ${cfg.pidDir} + chown -R ${cfg.user} ${cfg.pidDir} + ''; + + serviceConfig.ExecStart = "${mysql}/libexec/mysqld --defaults-extra-file=${myCnf} ${mysqldOptions}"; + + postStart = + '' + # Wait until the MySQL server is available for use + count=0 + while [ ! -e /tmp/mysql.sock ] + do + if [ $count -eq 30 ] + then + echo "Tried 30 times, giving up..." + exit 1 + fi + + echo "MySQL daemon not yet started. Waiting for 1 second..." + count=$((count++)) + sleep 1 + done + + if [ -f /tmp/mysql_init ] + then + ${concatMapStrings (database: + '' + # Create initial databases + if ! test -e "${cfg.dataDir}/${database.name}"; then + echo "Creating initial database: ${database.name}" + ( echo "create database ${database.name};" + echo "use ${database.name};" + + if [ -f "${database.schema}" ] + then + cat ${database.schema} + elif [ -d "${database.schema}" ] + then + cat ${database.schema}/mysql-databases/*.sql + fi + ) | ${mysql}/bin/mysql -u root -N + fi + '') cfg.initialDatabases} + + ${optionalString (cfg.initialScript != null) + '' + # Execute initial script + cat ${cfg.initialScript} | ${mysql}/bin/mysql -u root -N + ''} + + ${optionalString (cfg.rootPassword != null) + '' + # Change root password + + ( echo "use mysql;" + echo "update user set Password=password('$(cat ${cfg.rootPassword})') where User='root';" + echo "flush privileges;" + ) | ${mysql}/bin/mysql -u root -N + ''} + + rm /tmp/mysql_init + fi + ''; # */ + + serviceConfig.ExecStop = + "${mysql}/bin/mysqladmin ${optionalString (cfg.rootPassword != null) "--user=root --password=\"$(cat ${cfg.rootPassword})\""} shutdown"; + }; + + }; + +} diff --git a/nixos/modules/services/databases/mysql55.nix b/nixos/modules/services/databases/mysql55.nix new file mode 100644 index 00000000000..46148d68f4c --- /dev/null +++ b/nixos/modules/services/databases/mysql55.nix @@ -0,0 +1,248 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + + cfg = config.services.mysql55; + + mysql = cfg.package; + + pidFile = "${cfg.pidDir}/mysqld.pid"; + + mysqldOptions = + "--user=${cfg.user} --datadir=${cfg.dataDir} " + + "--pid-file=${pidFile}"; + + myCnf = pkgs.writeText "my.cnf" + '' + [mysqld] + ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "log-bin=mysql-bin"} + ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "server-id = ${toString cfg.replication.serverId}"} + ${cfg.extraOptions} + ''; + +in + +{ + + ###### interface + + options = { + + services.mysql55 = { + + enable = mkOption { + default = false; + description = " + Whether to enable the MySQL server. + "; + }; + + package = mkOption { + default = pkgs.mysql55; + description = " + Which MySQL derivation to use. + "; + }; + + port = mkOption { + default = "3306"; + description = "Port of MySQL"; + }; + + user = mkOption { + default = "mysql"; + description = "User account under which MySQL runs"; + }; + + dataDir = mkOption { + default = "/var/mysql"; # !!! should be /var/db/mysql + description = "Location where MySQL stores its table files"; + }; + + pidDir = mkOption { + default = "/var/run/mysql"; + description = "Location of the file which stores the PID of the MySQL server"; + }; + + extraOptions = mkOption { + default = ""; + example = '' + key_buffer_size = 6G + table_cache = 1600 + log-error = /var/log/mysql_err.log + ''; + description = '' + Provide extra options to the MySQL configuration file. + + Please note, that these options are added to the + <literal>[mysqld]</literal> section so you don't need to explicitly + state it again. + ''; + }; + + initialDatabases = mkOption { + default = []; + description = "List of database names and their initial schemas that should be used to create databases on the first startup of MySQL"; + example = [ + { name = "foodatabase"; schema = ./foodatabase.sql; } + { name = "bardatabase"; schema = ./bardatabase.sql; } + ]; + }; + + initialScript = mkOption { + default = null; + description = "A file containing SQL statements to be executed on the first startup. Can be used for granting certain permissions on the database"; + }; + + # FIXME: remove this option; it's a really bad idea. + rootPassword = mkOption { + default = null; + description = "Path to a file containing the root password, modified on the first startup. Not specifying a root password will leave the root password empty."; + }; + + replication = { + role = mkOption { + default = "none"; + description = "Role of the MySQL server instance. Can be either: master, slave or none"; + }; + + serverId = mkOption { + default = 1; + description = "Id of the MySQL server instance. This number must be unique for each instance"; + }; + + masterHost = mkOption { + description = "Hostname of the MySQL master server"; + }; + + masterUser = mkOption { + description = "Username of the MySQL replication user"; + }; + + masterPassword = mkOption { + description = "Password of the MySQL replication user"; + }; + + masterPort = mkOption { + default = 3306; + description = "Port number on which the MySQL master server runs"; + }; + }; + }; + + }; + + + ###### implementation + + config = mkIf config.services.mysql55.enable { + + users.extraUsers.mysql = { + description = "MySQL server user"; + group = "mysql"; + uid = config.ids.uids.mysql; + }; + + users.extraGroups.mysql.gid = config.ids.gids.mysql; + + environment.systemPackages = [mysql]; + + systemd.services.mysql = + { description = "MySQL Server"; + + wantedBy = [ "multi-user.target" ]; + + unitConfig.RequiresMountsFor = "${cfg.dataDir}"; + + preStart = + '' + if ! test -e ${cfg.dataDir}/mysql; then + mkdir -m 0700 -p ${cfg.dataDir} + chown -R ${cfg.user} ${cfg.dataDir} + ${mysql}/bin/mysql_install_db ${mysqldOptions} + touch /tmp/mysql_init + fi + + mkdir -m 0700 -p ${cfg.pidDir} + chown -R ${cfg.user} ${cfg.pidDir} + ''; + + serviceConfig.ExecStart = "${mysql}/bin/mysqld --defaults-extra-file=${myCnf} ${mysqldOptions}"; + + postStart = + '' + # Wait until the MySQL server is available for use + count=0 + while [ ! -e /tmp/mysql.sock ] + do + if [ $count -eq 30 ] + then + echo "Tried 30 times, giving up..." + exit 1 + fi + + echo "MySQL daemon not yet started. Waiting for 1 second..." + count=$((count++)) + sleep 1 + done + + if [ -f /tmp/mysql_init ] + then + ${concatMapStrings (database: + '' + # Create initial databases + if ! test -e "${cfg.dataDir}/${database.name}"; then + echo "Creating initial database: ${database.name}" + ( echo "create database ${database.name};" + echo "use ${database.name};" + + if [ -f "${database.schema}" ] + then + cat ${database.schema} + elif [ -d "${database.schema}" ] + then + cat ${database.schema}/mysql-databases/*.sql + fi + ) | ${mysql}/bin/mysql -u root -N + fi + '') cfg.initialDatabases} + + ${optionalString (cfg.replication.role == "slave") + '' + # Set up the replication master + + ( echo "stop slave;" + echo "change master to master_host='${cfg.replication.masterHost}', master_user='${cfg.replication.masterUser}', master_password='${cfg.replication.masterPassword}';" + echo "start slave;" + ) | ${mysql}/bin/mysql -u root -N + ''} + + ${optionalString (cfg.initialScript != null) + '' + # Execute initial script + cat ${cfg.initialScript} | ${mysql}/bin/mysql -u root -N + ''} + + ${optionalString (cfg.rootPassword != null) + '' + # Change root password + + ( echo "use mysql;" + echo "update user set Password=password('$(cat ${cfg.rootPassword})') where User='root';" + echo "flush privileges;" + ) | ${mysql}/bin/mysql -u root -N + ''} + + rm /tmp/mysql_init + fi + ''; # */ + + serviceConfig.ExecStop = + "${mysql}/bin/mysqladmin ${optionalString (cfg.rootPassword != null) "--user=root --password=\"$(cat ${cfg.rootPassword})\""} shutdown"; + }; + + }; + +} diff --git a/nixos/modules/services/databases/openldap.nix b/nixos/modules/services/databases/openldap.nix new file mode 100644 index 00000000000..a4dd30be1fb --- /dev/null +++ b/nixos/modules/services/databases/openldap.nix @@ -0,0 +1,58 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + + cfg = config.services.openldap; + openldap = pkgs.openldap; + + configFile = pkgs.writeText "slapd.conf" cfg.extraConfig; + +in + +{ + + ###### interface + + options = { + + services.openldap = { + + enable = mkOption { + default = false; + description = " + Whether to enable the ldap server. + "; + }; + + extraConfig = mkOption { + default = ""; + description = " + sldapd.conf configuration + "; + }; + }; + + }; + + + ###### implementation + + config = mkIf config.services.openldap.enable { + + environment.systemPackages = [ openldap ]; + + systemd.services.openldap = { + description = "LDAP server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + preStart = '' + mkdir -p /var/run/slapd + ''; + serviceConfig.ExecStart = "${openldap}/libexec/slapd -d 0 -f ${configFile}"; + }; + + }; + +} diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix new file mode 100644 index 00000000000..fc6b5b167b8 --- /dev/null +++ b/nixos/modules/services/databases/postgresql.nix @@ -0,0 +1,232 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + + cfg = config.services.postgresql; + + # see description of extraPlugins + postgresqlAndPlugins = pg: + if cfg.extraPlugins == [] then pg + else pkgs.buildEnv { + name = "postgresql-and-plugins-${(builtins.parseDrvName pg.name).version}"; + paths = [ pg ] ++ cfg.extraPlugins; + postBuild = + '' + mkdir -p $out/bin + rm $out/bin/{pg_config,postgres,pg_ctl} + cp --target-directory=$out/bin ${pg}/bin/{postgres,pg_config,pg_ctl} + ''; + }; + + postgresql = postgresqlAndPlugins cfg.package; + + flags = optional cfg.enableTCPIP "-i"; + + # The main PostgreSQL configuration file. + configFile = pkgs.writeText "postgresql.conf" + '' + hba_file = '${pkgs.writeText "pg_hba.conf" cfg.authentication}' + ident_file = '${pkgs.writeText "pg_ident.conf" cfg.identMap}' + log_destination = 'stderr' + ${cfg.extraConfig} + ''; + + pre84 = versionOlder (builtins.parseDrvName postgresql.name).version "8.4"; + +in + +{ + + ###### interface + + options = { + + services.postgresql = { + + enable = mkOption { + default = false; + description = '' + Whether to run PostgreSQL. + ''; + }; + + package = mkOption { + example = literalExample "pkgs.postgresql92"; + description = '' + PostgreSQL package to use. + ''; + }; + + port = mkOption { + default = "5432"; + description = '' + Port for PostgreSQL. + ''; + }; + + dataDir = mkOption { + default = "/var/db/postgresql"; + description = '' + Data directory for PostgreSQL. + ''; + }; + + authentication = mkOption { + default = ""; + description = '' + Defines how users authenticate themselves to the server. + ''; + }; + + identMap = mkOption { + default = ""; + description = '' + Defines the mapping from system users to database users. + ''; + }; + + initialScript = mkOption { + default = null; + type = types.nullOr types.path; + description = '' + A file containing SQL statements to execute on first startup. + ''; + }; + + authMethod = mkOption { + default = " ident sameuser "; + description = '' + How to authorize users. + Note: ident needs absolute trust to all allowed client hosts. + ''; + }; + + enableTCPIP = mkOption { + default = false; + description = '' + Whether to run PostgreSQL with -i flag to enable TCP/IP connections. + ''; + }; + + extraPlugins = mkOption { + default = []; + example = "pkgs.postgis"; # of course don't use a string here! + description = '' + When this list contains elements a new store path is created. + PostgreSQL and the elments are symlinked into it. Then pg_config, + postgres and pc_ctl are copied to make them use the new + $out/lib directory as pkglibdir. This makes it possible to use postgis + without patching the .sql files which reference $libdir/postgis-1.5. + ''; + # Note: the duplication of executables is about 4MB size. + # So a nicer solution was patching postgresql to allow setting the + # libdir explicitely. + }; + + extraConfig = mkOption { + default = ""; + description = "Additional text to be appended to <filename>postgresql.conf</filename>."; + }; + + recoveryConfig = mkOption { + default = null; + type = types.nullOr types.string; + description = '' + Values to put into recovery.conf file. + ''; + }; + }; + + }; + + + ###### implementation + + config = mkIf config.services.postgresql.enable { + + services.postgresql.authentication = + '' + # Generated file; do not edit! + local all all ident ${optionalString pre84 "sameuser"} + host all all 127.0.0.1/32 md5 + host all all ::1/128 md5 + ''; + + users.extraUsers.postgres = + { name = "postgres"; + uid = config.ids.uids.postgres; + group = "postgres"; + description = "PostgreSQL server user"; + }; + + users.extraGroups.postgres.gid = config.ids.gids.postgres; + + environment.systemPackages = [postgresql]; + + systemd.services.postgresql = + { description = "PostgreSQL Server"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + environment.PGDATA = cfg.dataDir; + + path = [ pkgs.su postgresql ]; + + preStart = + '' + # Initialise the database. + if ! test -e ${cfg.dataDir}; then + mkdir -m 0700 -p ${cfg.dataDir} + chown -R postgres ${cfg.dataDir} + su -s ${pkgs.stdenv.shell} postgres -c 'initdb -U root' + rm -f ${cfg.dataDir}/*.conf + touch "${cfg.dataDir}/.first_startup" + fi + + ln -sfn "${configFile}" "${cfg.dataDir}/postgresql.conf" + ${optionalString (cfg.recoveryConfig != null) '' + ln -sfn "${pkgs.writeText "recovery.conf" cfg.recoveryConfig}" \ + "${cfg.dataDir}/recovery.conf" + ''} + ''; # */ + + serviceConfig = + { ExecStart = "@${postgresql}/bin/postgres postgres ${toString flags}"; + User = "postgres"; + Group = "postgres"; + PermissionsStartOnly = true; + + # Shut down Postgres using SIGINT ("Fast Shutdown mode"). See + # http://www.postgresql.org/docs/current/static/server-shutdown.html + KillSignal = "SIGINT"; + + # Give Postgres a decent amount of time to clean up after + # receiving systemd's SIGINT. + TimeoutSec = 120; + }; + + # Wait for PostgreSQL to be ready to accept connections. + postStart = + '' + while ! psql 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) '' + cat "${cfg.initialScript}" | psql postgres + ''} + rm -f "${cfg.dataDir}/.first_startup" + fi + ''; + + unitConfig.RequiresMountsFor = "${cfg.dataDir}"; + }; + + }; + +} diff --git a/nixos/modules/services/databases/redis.nix b/nixos/modules/services/databases/redis.nix new file mode 100644 index 00000000000..5bc58c73bd6 --- /dev/null +++ b/nixos/modules/services/databases/redis.nix @@ -0,0 +1,216 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + cfg = config.services.redis; + redisBool = b: if b then "yes" else "no"; + condOption = name: value: if value != null then "${name} ${toString value}" else ""; + + redisConfig = pkgs.writeText "redis.conf" '' + pidfile ${cfg.pidFile} + port ${toString cfg.port} + ${condOption "bind" cfg.bind} + ${condOption "unixsocket" cfg.unixSocket} + loglevel ${cfg.logLevel} + logfile ${cfg.logfile} + databases ${toString cfg.databases} + ${concatMapStrings (d: "save ${toString (builtins.elemAt d 0)} ${toString (builtins.elemAt d 1)}\n") cfg.save} + dbfilename ${cfg.dbFilename} + dir ${toString cfg.dbpath} + ${if cfg.slaveOf != null then "slaveof ${cfg.slaveOf.ip} ${toString cfg.slaveOf.port}" else ""} + ${condOption "masterauth" cfg.masterAuth} + ${condOption "requirepass" cfg.requirePass} + appendOnly ${redisBool cfg.appendOnly} + appendfsync ${cfg.appendFsync} + slowlog-log-slower-than ${toString cfg.slowLogLogSlowerThan} + slowlog-max-len ${toString cfg.slowLogMaxLen} + ${cfg.extraConfig} + ''; +in +{ + + ###### interface + + options = { + + services.redis = { + + enable = mkOption { + default = false; + description = "Whether to enable the Redis server."; + }; + + package = mkOption { + default = pkgs.redis; + description = "Which Redis derivation to use."; + }; + + user = mkOption { + default = "redis"; + description = "User account under which Redis runs"; + }; + + pidFile = mkOption { + default = "/var/lib/redis/redis.pid"; + description = ""; + }; + + port = mkOption { + default = 6379; + description = "The port for Redis to listen to"; + type = with types; int; + }; + + bind = mkOption { + default = null; # All interfaces + description = "The IP interface to bind to"; + example = "127.0.0.1"; + }; + + unixSocket = mkOption { + default = null; + description = "The path to the socket to bind to"; + example = "/var/run/redis.sock"; + }; + + logLevel = mkOption { + default = "notice"; # debug, verbose, notice, warning + example = "debug"; + description = "Specify the server verbosity level, options: debug, verbose, notice, warning"; + type = with types; string; + }; + + logfile = mkOption { + default = "stdout"; + description = "Specify the log file name. Also 'stdout' can be used to force Redis to log on the standard output."; + example = "/var/log/redis.log"; + type = with types; string; + }; + + databases = mkOption { + default = 16; + description = "Set the number of databases."; + type = with types; int; + }; + + save = mkOption { + default = [ [900 1] [300 10] [60 10000] ]; + description = "The schedule in which data is persisted to disk, represented as a list of lists where the first element represent the amount of seconds and the second the number of changes."; + example = [ [900 1] [300 10] [60 10000] ]; + }; + + dbFilename = mkOption { + default = "dump.rdb"; + description = "The filename where to dump the DB"; + type = with types; string; + }; + + dbpath = mkOption { + default = "/var/lib/redis"; + description = "The DB will be written inside this directory, with the filename specified using the 'dbFilename' configuration"; + type = with types; string; + }; + + slaveOf = mkOption { + default = null; # { ip, port } + description = "An attribute set with two attributes: ip and port to which this redis instance acts as a slave"; + example = { ip = "192.168.1.100"; port = 6379; }; + }; + + masterAuth = mkOption { + default = null; + description = ''If the master is password protected (using the requirePass configuration) + it is possible to tell the slave to authenticate before starting the replication synchronization + process, otherwise the master will refuse the slave request. + (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)''; + }; + + requirePass = mkOption { + default = null; + description = "Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)"; + example = "letmein!"; + }; + + appendOnly = mkOption { + default = false; + description = "By default data is only periodically persisted to disk, enable this option to use an append-only file for improved persistence."; + type = with types; bool; + }; + + appendOnlyFilename = mkOption { + default = "appendonly.aof"; + description = "Filename for the append-only file (stored inside of dbpath)"; + type = with types; string; + }; + + appendFsync = mkOption { + default = "everysec"; # no, always, everysec + description = "How often to fsync the append-only log, options: no, always, everysec"; + type = with types; string; + }; + + slowLogLogSlowerThan = mkOption { + default = 10000; + description = "Log queries whose execution take longer than X in milliseconds"; + example = 1000; + type = with types; int; + }; + + slowLogMaxLen = mkOption { + default = 128; + description = "Maximum number of items to keep in slow log"; + type = with types; int; + }; + + extraConfig = mkOption { + default = ""; + description = "Extra configuration options for redis.conf"; + type = with types; string; + }; + }; + + }; + + + ###### implementation + + config = mkIf config.services.redis.enable { + + users.extraUsers = singleton + { name = cfg.user; + description = "Redis database user"; + }; + + environment.systemPackages = [ cfg.package ]; + + systemd.services.redis_init = + { description = "Redis server initialisation"; + + wantedBy = [ "redis.service" ]; + before = [ "redis.service" ]; + + serviceConfig.Type = "oneshot"; + + script = '' + if ! test -e ${cfg.dbpath}; then + install -d -m0700 -o ${cfg.user} ${cfg.dbpath} + fi + ''; + }; + + systemd.services.redis = + { description = "Redis server"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + ExecStart = "${cfg.package}/bin/redis-server ${redisConfig}"; + User = cfg.user; + }; + }; + + }; + +} diff --git a/nixos/modules/services/databases/virtuoso.nix b/nixos/modules/services/databases/virtuoso.nix new file mode 100644 index 00000000000..6a29fc13211 --- /dev/null +++ b/nixos/modules/services/databases/virtuoso.nix @@ -0,0 +1,98 @@ +{ config, pkgs, ... }: +let + cfg = config.services.virtuoso; + virtuosoUser = "virtuoso"; + stateDir = "/var/lib/virtuoso"; +in +with pkgs.lib; +{ + + ###### interface + + options = { + + services.virtuoso = { + + enable = mkOption { + default = false; + description = "Whether to enable Virtuoso Opensource database server."; + }; + + config = mkOption { + default = ""; + description = "Extra options to put into Virtuoso configuration file."; + }; + + parameters = mkOption { + default = ""; + description = "Extra options to put into [Parameters] section of Virtuoso configuration file."; + }; + + listenAddress = mkOption { + default = "1111"; + example = "myserver:1323"; + description = "ip:port or port to listen on."; + }; + + httpListenAddress = mkOption { + default = null; + example = "myserver:8080"; + description = "ip:port or port for Virtuoso HTTP server to listen on."; + }; + + dirsAllowed = mkOption { + default = null; + example = "/www, /home/"; + description = "A list of directories Virtuoso is allowed to access"; + }; + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + users.extraUsers = singleton + { name = virtuosoUser; + uid = config.ids.uids.virtuoso; + description = "virtuoso user"; + home = stateDir; + }; + + jobs.virtuoso = { + name = "virtuoso"; + startOn = "filesystem"; + + preStart = '' + mkdir -p ${stateDir} + chown ${virtuosoUser} ${stateDir} + ''; + + script = '' + cd ${stateDir} + ${pkgs.virtuoso}/bin/virtuoso-t +foreground +configfile ${pkgs.writeText "virtuoso.ini" cfg.config} + ''; + }; + + services.virtuoso.config = '' + [Database] + DatabaseFile=${stateDir}/x-virtuoso.db + TransactionFile=${stateDir}/x-virtuoso.trx + ErrorLogFile=${stateDir}/x-virtuoso.log + xa_persistent_file=${stateDir}/x-virtuoso.pxa + + [Parameters] + ServerPort=${cfg.listenAddress} + RunAs=${virtuosoUser} + ${optionalString (cfg.dirsAllowed != null) "DirsAllowed=${cfg.dirsAllowed}"} + ${cfg.parameters} + + [HTTPServer] + ${optionalString (cfg.httpListenAddress != null) "ServerPort=${cfg.httpListenAddress}"} + ''; + + }; + +} |