 acb1134074
			
		
	
	
		acb1134074
		
			
		
	
	
	
	
		
			
			Fixes an evaluation regression introduced by a case-typo in de6e5ea8152b29c975096d7d4b7d0938874ee37a Signed-off-by: Austin Seipp <aseipp@pobox.com>
		
			
				
	
	
		
			430 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			430 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
| { config, lib, pkgs, ... }:
 | |
| 
 | |
| with lib;
 | |
| 
 | |
| let
 | |
|   cfg = config.services.foundationdb;
 | |
|   pkg = cfg.package;
 | |
| 
 | |
|   # used for initial cluster configuration
 | |
|   initialIpAddr = if (cfg.publicAddress != "auto") then cfg.publicAddress else "127.0.0.1";
 | |
| 
 | |
|   fdbServers = n:
 | |
|     concatStringsSep "\n" (map (x: "[fdbserver.${toString (x+cfg.listenPortStart)}]") (range 0 (n - 1)));
 | |
| 
 | |
|   backupAgents = n:
 | |
|     concatStringsSep "\n" (map (x: "[backup_agent.${toString x}]") (range 1 n));
 | |
| 
 | |
|   configFile = pkgs.writeText "foundationdb.conf" ''
 | |
|     [general]
 | |
|     cluster_file  = /etc/foundationdb/fdb.cluster
 | |
| 
 | |
|     [fdbmonitor]
 | |
|     restart_delay = ${toString cfg.restartDelay}
 | |
|     user          = ${cfg.user}
 | |
|     group         = ${cfg.group}
 | |
| 
 | |
|     [fdbserver]
 | |
|     command        = ${pkg}/bin/fdbserver
 | |
|     public_address = ${cfg.publicAddress}:$ID
 | |
|     listen_address = ${cfg.listenAddress}
 | |
|     datadir        = ${cfg.dataDir}/$ID
 | |
|     logdir         = ${cfg.logDir}
 | |
|     logsize        = ${cfg.logSize}
 | |
|     maxlogssize    = ${cfg.maxLogSize}
 | |
|     ${optionalString (cfg.class != null) "class = ${cfg.class}"}
 | |
|     memory         = ${cfg.memory}
 | |
|     storage_memory = ${cfg.storageMemory}
 | |
| 
 | |
|     ${optionalString (lib.versionAtLeast cfg.package.version "6.1") ''
 | |
|     trace_format   = ${cfg.traceFormat}
 | |
|     ''}
 | |
| 
 | |
|     ${optionalString (cfg.tls != null) ''
 | |
|       tls_plugin           = ${pkg}/libexec/plugins/FDBLibTLS.so
 | |
|       tls_certificate_file = ${cfg.tls.certificate}
 | |
|       tls_key_file         = ${cfg.tls.key}
 | |
|       tls_verify_peers     = ${cfg.tls.allowedPeers}
 | |
|     ''}
 | |
| 
 | |
|     ${optionalString (cfg.locality.machineId    != null) "locality_machineid=${cfg.locality.machineId}"}
 | |
|     ${optionalString (cfg.locality.zoneId       != null) "locality_zoneid=${cfg.locality.zoneId}"}
 | |
|     ${optionalString (cfg.locality.datacenterId != null) "locality_dcid=${cfg.locality.datacenterId}"}
 | |
|     ${optionalString (cfg.locality.dataHall     != null) "locality_data_hall=${cfg.locality.dataHall}"}
 | |
| 
 | |
|     ${fdbServers cfg.serverProcesses}
 | |
| 
 | |
|     [backup_agent]
 | |
|     command = ${pkg}/libexec/backup_agent
 | |
|     ${backupAgents cfg.backupProcesses}
 | |
|   '';
 | |
| in
 | |
| {
 | |
|   options.services.foundationdb = {
 | |
| 
 | |
|     enable = mkEnableOption "FoundationDB Server";
 | |
| 
 | |
|     package = mkOption {
 | |
|       type        = types.package;
 | |
|       description = ''
 | |
|         The FoundationDB package to use for this server. This must be specified by the user
 | |
|         in order to ensure migrations and upgrades are controlled appropriately.
 | |
|       '';
 | |
|     };
 | |
| 
 | |
|     publicAddress = mkOption {
 | |
|       type        = types.str;
 | |
|       default     = "auto";
 | |
|       description = "Publicly visible IP address of the process. Port is determined by process ID";
 | |
|     };
 | |
| 
 | |
|     listenAddress = mkOption {
 | |
|       type        = types.str;
 | |
|       default     = "public";
 | |
|       description = "Publicly visible IP address of the process. Port is determined by process ID";
 | |
|     };
 | |
| 
 | |
|     listenPortStart = mkOption {
 | |
|       type          = types.int;
 | |
|       default       = 4500;
 | |
|       description   = ''
 | |
|         Starting port number for database listening sockets. Every FDB process binds to a
 | |
|         subsequent port, to this number reflects the start of the overall range. e.g. having
 | |
|         8 server processes will use all ports between 4500 and 4507.
 | |
|       '';
 | |
|     };
 | |
| 
 | |
|     openFirewall = mkOption {
 | |
|       type        = types.bool;
 | |
|       default     = false;
 | |
|       description = ''
 | |
|         Open the firewall ports corresponding to FoundationDB processes and coordinators
 | |
|         using <option>config.networking.firewall.*</option>.
 | |
|       '';
 | |
|     };
 | |
| 
 | |
|     dataDir = mkOption {
 | |
|       type        = types.path;
 | |
|       default     = "/var/lib/foundationdb";
 | |
|       description = "Data directory. All cluster data will be put under here.";
 | |
|     };
 | |
| 
 | |
|     logDir = mkOption {
 | |
|       type        = types.path;
 | |
|       default     = "/var/log/foundationdb";
 | |
|       description = "Log directory.";
 | |
|     };
 | |
| 
 | |
|     user = mkOption {
 | |
|       type        = types.str;
 | |
|       default     = "foundationdb";
 | |
|       description = "User account under which FoundationDB runs.";
 | |
|     };
 | |
| 
 | |
|     group = mkOption {
 | |
|       type        = types.str;
 | |
|       default     = "foundationdb";
 | |
|       description = "Group account under which FoundationDB runs.";
 | |
|     };
 | |
| 
 | |
|     class = mkOption {
 | |
|       type        = types.nullOr (types.enum [ "storage" "transaction" "stateless" ]);
 | |
|       default     = null;
 | |
|       description = "Process class";
 | |
|     };
 | |
| 
 | |
|     restartDelay = mkOption {
 | |
|       type = types.int;
 | |
|       default = 10;
 | |
|       description = "Number of seconds to wait before restarting servers.";
 | |
|     };
 | |
| 
 | |
|     logSize = mkOption {
 | |
|       type        = types.string;
 | |
|       default     = "10MiB";
 | |
|       description = ''
 | |
|         Roll over to a new log file after the current log file
 | |
|         reaches the specified size.
 | |
|       '';
 | |
|     };
 | |
| 
 | |
|     maxLogSize = mkOption {
 | |
|       type        = types.string;
 | |
|       default     = "100MiB";
 | |
|       description = ''
 | |
|         Delete the oldest log file when the total size of all log
 | |
|         files exceeds the specified size. If set to 0, old log files
 | |
|         will not be deleted.
 | |
|       '';
 | |
|     };
 | |
| 
 | |
|     serverProcesses = mkOption {
 | |
|       type = types.int;
 | |
|       default = 1;
 | |
|       description = "Number of fdbserver processes to run.";
 | |
|     };
 | |
| 
 | |
|     backupProcesses = mkOption {
 | |
|       type = types.int;
 | |
|       default = 1;
 | |
|       description = "Number of backup_agent processes to run for snapshots.";
 | |
|     };
 | |
| 
 | |
|     memory = mkOption {
 | |
|       type        = types.string;
 | |
|       default     = "8GiB";
 | |
|       description = ''
 | |
|         Maximum memory used by the process. The default value is
 | |
|         <literal>8GiB</literal>. When specified without a unit,
 | |
|         <literal>MiB</literal> is assumed. This parameter does not
 | |
|         change the memory allocation of the program. Rather, it sets
 | |
|         a hard limit beyond which the process will kill itself and
 | |
|         be restarted. The default value of <literal>8GiB</literal>
 | |
|         is double the intended memory usage in the default
 | |
|         configuration (providing an emergency buffer to deal with
 | |
|         memory leaks or similar problems). It is not recommended to
 | |
|         decrease the value of this parameter below its default
 | |
|         value. It may be increased if you wish to allocate a very
 | |
|         large amount of storage engine memory or cache. In
 | |
|         particular, when the <literal>storageMemory</literal>
 | |
|         parameter is increased, the <literal>memory</literal>
 | |
|         parameter should be increased by an equal amount.
 | |
|       '';
 | |
|     };
 | |
| 
 | |
|     storageMemory = mkOption {
 | |
|       type        = types.string;
 | |
|       default     = "1GiB";
 | |
|       description = ''
 | |
|         Maximum memory used for data storage. The default value is
 | |
|         <literal>1GiB</literal>. When specified without a unit,
 | |
|         <literal>MB</literal> is assumed. Clusters using the memory
 | |
|         storage engine will be restricted to using this amount of
 | |
|         memory per process for purposes of data storage. Memory
 | |
|         overhead associated with storing the data is counted against
 | |
|         this total. If you increase the
 | |
|         <literal>storageMemory</literal>, you should also increase
 | |
|         the <literal>memory</literal> parameter by the same amount.
 | |
|       '';
 | |
|     };
 | |
| 
 | |
|     tls = mkOption {
 | |
|       default = null;
 | |
|       description = ''
 | |
|         FoundationDB Transport Security Layer (TLS) settings.
 | |
|       '';
 | |
| 
 | |
|       type = types.nullOr (types.submodule ({
 | |
|         options = {
 | |
|           certificate = mkOption {
 | |
|             type = types.str;
 | |
|             description = ''
 | |
|               Path to the TLS certificate file. This certificate will
 | |
|               be offered to, and may be verified by, clients.
 | |
|             '';
 | |
|           };
 | |
| 
 | |
|           key = mkOption {
 | |
|             type = types.str;
 | |
|             description = "Private key file for the certificate.";
 | |
|           };
 | |
| 
 | |
|           allowedPeers = mkOption {
 | |
|             type = types.str;
 | |
|             default = "Check.Valid=1,Check.Unexpired=1";
 | |
|             description = ''
 | |
| 	      "Peer verification string". This may be used to adjust which TLS
 | |
|               client certificates a server will accept, as a form of user
 | |
|               authorization; for example, it may only accept TLS clients who
 | |
|               offer a certificate abiding by some locality or organization name.
 | |
| 
 | |
|               For more information, please see the FoundationDB documentation.
 | |
|             '';
 | |
|           };
 | |
|         };
 | |
|       }));
 | |
|     };
 | |
| 
 | |
|     locality = mkOption {
 | |
|       default = {
 | |
|         machineId    = null;
 | |
|         zoneId       = null;
 | |
|         datacenterId = null;
 | |
|         dataHall     = null;
 | |
|       };
 | |
| 
 | |
|       description = ''
 | |
|         FoundationDB locality settings.
 | |
|       '';
 | |
| 
 | |
|       type = types.submodule ({
 | |
|         options = {
 | |
|           machineId = mkOption {
 | |
|             default = null;
 | |
|             type = types.nullOr types.str;
 | |
|             description = ''
 | |
|               Machine identifier key. All processes on a machine should share a
 | |
|               unique id. By default, processes on a machine determine a unique id to share.
 | |
|               This does not generally need to be set.
 | |
|             '';
 | |
|           };
 | |
| 
 | |
|           zoneId = mkOption {
 | |
|             default = null;
 | |
|             type = types.nullOr types.str;
 | |
|             description = ''
 | |
|               Zone identifier key. Processes that share a zone id are
 | |
|               considered non-unique for the purposes of data replication.
 | |
|               If unset, defaults to machine id.
 | |
|             '';
 | |
|           };
 | |
| 
 | |
|           datacenterId = mkOption {
 | |
|             default = null;
 | |
|             type = types.nullOr types.str;
 | |
|             description = ''
 | |
|               Data center identifier key. All processes physically located in a
 | |
|               data center should share the id. If you are depending on data
 | |
|               center based replication this must be set on all processes.
 | |
|             '';
 | |
|           };
 | |
| 
 | |
|           dataHall = mkOption {
 | |
|             default = null;
 | |
|             type = types.nullOr types.str;
 | |
|             description = ''
 | |
|               Data hall identifier key. All processes physically located in a
 | |
|               data hall should share the id. If you are depending on data
 | |
|               hall based replication this must be set on all processes.
 | |
|             '';
 | |
|           };
 | |
|         };
 | |
|       });
 | |
|     };
 | |
| 
 | |
|     extraReadWritePaths = mkOption {
 | |
|       default = [ ];
 | |
|       type = types.listOf types.path;
 | |
|       description = ''
 | |
|         An extra set of filesystem paths that FoundationDB can read to
 | |
|         and write from. By default, FoundationDB runs under a heavily
 | |
|         namespaced systemd environment without write access to most of
 | |
|         the filesystem outside of its data and log directories. By
 | |
|         adding paths to this list, the set of writeable paths will be
 | |
|         expanded. This is useful for allowing e.g. backups to local files,
 | |
|         which must be performed on behalf of the foundationdb service.
 | |
|       '';
 | |
|     };
 | |
| 
 | |
|     pidfile = mkOption {
 | |
|       type        = types.path;
 | |
|       default     = "/run/foundationdb.pid";
 | |
|       description = "Path to pidfile for fdbmonitor.";
 | |
|     };
 | |
| 
 | |
|     traceFormat = mkOption {
 | |
|       type = types.enum [ "xml" "json" ];
 | |
|       default = "xml";
 | |
|       description = "Trace logging format.";
 | |
|     };
 | |
|   };
 | |
| 
 | |
|   config = mkIf cfg.enable {
 | |
|     assertions = [
 | |
|       { assertion = lib.versionOlder cfg.package.version "6.1" -> cfg.traceFormat == "xml";
 | |
|         message = ''
 | |
|           Versions of FoundationDB before 6.1 do not support configurable trace formats (only XML is supported).
 | |
|           This option has no effect for version '' + cfg.package.version + '', and enabling it is an error.
 | |
|         '';
 | |
|       }
 | |
|     ];
 | |
| 
 | |
|     environment.systemPackages = [ pkg ];
 | |
| 
 | |
|     users.users = optionalAttrs (cfg.user == "foundationdb") (singleton
 | |
|       { name        = "foundationdb";
 | |
|         description = "FoundationDB User";
 | |
|         uid         = config.ids.uids.foundationdb;
 | |
|         group       = cfg.group;
 | |
|       });
 | |
| 
 | |
|     users.groups = optionalAttrs (cfg.group == "foundationdb") (singleton
 | |
|       { name = "foundationdb";
 | |
|         gid  = config.ids.gids.foundationdb;
 | |
|       });
 | |
| 
 | |
|     networking.firewall.allowedTCPPortRanges = mkIf cfg.openFirewall
 | |
|       [ { from = cfg.listenPortStart;
 | |
|           to = (cfg.listenPortStart + cfg.serverProcesses) - 1;
 | |
|         }
 | |
|       ];
 | |
| 
 | |
|     systemd.tmpfiles.rules = [
 | |
|       "d /etc/foundationdb 0755 ${cfg.user} ${cfg.group} - -"
 | |
|       "d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -"
 | |
|       "d '${cfg.logDir}' 0770 ${cfg.user} ${cfg.group} - -"
 | |
|       "F '${cfg.pidfile}' - ${cfg.user} ${cfg.group} - -"
 | |
|     ];
 | |
| 
 | |
|     systemd.services.foundationdb = {
 | |
|       description             = "FoundationDB Service";
 | |
| 
 | |
|       after                   = [ "network.target" ];
 | |
|       wantedBy                = [ "multi-user.target" ];
 | |
|       unitConfig =
 | |
|         { RequiresMountsFor = "${cfg.dataDir} ${cfg.logDir}";
 | |
|         };
 | |
| 
 | |
|       serviceConfig =
 | |
|         let rwpaths = [ cfg.dataDir cfg.logDir cfg.pidfile "/etc/foundationdb" ]
 | |
|                    ++ cfg.extraReadWritePaths;
 | |
|         in
 | |
|         { Type       = "simple";
 | |
|           Restart    = "always";
 | |
|           RestartSec = 5;
 | |
|           User       = cfg.user;
 | |
|           Group      = cfg.group;
 | |
|           PIDFile    = "${cfg.pidfile}";
 | |
| 
 | |
|           PermissionsStartOnly = true;  # setup needs root perms
 | |
|           TimeoutSec           = 120;   # give reasonable time to shut down
 | |
| 
 | |
|           # Security options
 | |
|           NoNewPrivileges       = true;
 | |
|           ProtectHome           = true;
 | |
|           ProtectSystem         = "strict";
 | |
|           ProtectKernelTunables = true;
 | |
|           ProtectControlGroups  = true;
 | |
|           PrivateTmp            = true;
 | |
|           PrivateDevices        = true;
 | |
|           ReadWritePaths        = lib.concatStringsSep " " (map (x: "-" + x) rwpaths);
 | |
|         };
 | |
| 
 | |
|       path = [ pkg pkgs.coreutils ];
 | |
| 
 | |
|       preStart = ''
 | |
|         if [ ! -f /etc/foundationdb/fdb.cluster ]; then
 | |
|             cf=/etc/foundationdb/fdb.cluster
 | |
|             desc=$(tr -dc A-Za-z0-9 </dev/urandom 2>/dev/null | head -c8)
 | |
|             rand=$(tr -dc A-Za-z0-9 </dev/urandom 2>/dev/null | head -c8)
 | |
|             echo ''${desc}:''${rand}@${initialIpAddr}:${builtins.toString cfg.listenPortStart} > $cf
 | |
|             chmod 0664 $cf
 | |
|             touch "${cfg.dataDir}/.first_startup"
 | |
|         fi
 | |
|       '';
 | |
| 
 | |
|       script = "exec fdbmonitor --lockfile ${cfg.pidfile} --conffile ${configFile}";
 | |
| 
 | |
|       postStart = ''
 | |
|         if [ -e "${cfg.dataDir}/.first_startup" ]; then
 | |
|           fdbcli --exec "configure new single ssd"
 | |
|           rm -f "${cfg.dataDir}/.first_startup";
 | |
|         fi
 | |
|       '';
 | |
|     };
 | |
|   };
 | |
| 
 | |
|   meta.doc         = ./foundationdb.xml;
 | |
|   meta.maintainers = with lib.maintainers; [ thoughtpolice ];
 | |
| }
 |