at 18.09-beta 14 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.foundationdb; 7 pkg = cfg.package; 8 9 # used for initial cluster configuration 10 initialIpAddr = if (cfg.publicAddress != "auto") then cfg.publicAddress else "127.0.0.1"; 11 12 fdbServers = n: 13 concatStringsSep "\n" (map (x: "[fdbserver.${toString (x+cfg.listenPortStart)}]") (range 0 (n - 1))); 14 15 backupAgents = n: 16 concatStringsSep "\n" (map (x: "[backup_agent.${toString x}]") (range 1 n)); 17 18 configFile = pkgs.writeText "foundationdb.conf" '' 19 [general] 20 cluster_file = /etc/foundationdb/fdb.cluster 21 22 [fdbmonitor] 23 restart_delay = ${toString cfg.restartDelay} 24 user = ${cfg.user} 25 group = ${cfg.group} 26 27 [fdbserver] 28 command = ${pkg}/bin/fdbserver 29 public_address = ${cfg.publicAddress}:$ID 30 listen_address = ${cfg.listenAddress} 31 datadir = ${cfg.dataDir}/$ID 32 logdir = ${cfg.logDir} 33 logsize = ${cfg.logSize} 34 maxlogssize = ${cfg.maxLogSize} 35 ${optionalString (cfg.class != null) "class = ${cfg.class}"} 36 memory = ${cfg.memory} 37 storage_memory = ${cfg.storageMemory} 38 39 ${optionalString (cfg.tls != null) '' 40 tls_plugin = ${pkg}/libexec/plugins/FDBLibTLS.so 41 tls_certificate_file = ${cfg.tls.certificate} 42 tls_key_file = ${cfg.tls.key} 43 tls_verify_peers = ${cfg.tls.allowedPeers} 44 ''} 45 46 ${optionalString (cfg.locality.machineId != null) "locality_machineid=${cfg.locality.machineId}"} 47 ${optionalString (cfg.locality.zoneId != null) "locality_zoneid=${cfg.locality.zoneId}"} 48 ${optionalString (cfg.locality.datacenterId != null) "locality_dcid=${cfg.locality.datacenterId}"} 49 ${optionalString (cfg.locality.dataHall != null) "locality_data_hall=${cfg.locality.dataHall}"} 50 51 ${fdbServers cfg.serverProcesses} 52 53 [backup_agent] 54 command = ${pkg}/libexec/backup_agent 55 ${backupAgents cfg.backupProcesses} 56 ''; 57in 58{ 59 options.services.foundationdb = { 60 61 enable = mkEnableOption "FoundationDB Server"; 62 63 package = mkOption { 64 type = types.package; 65 description = '' 66 The FoundationDB package to use for this server. This must be specified by the user 67 in order to ensure migrations and upgrades are controlled appropriately. 68 ''; 69 }; 70 71 publicAddress = mkOption { 72 type = types.str; 73 default = "auto"; 74 description = "Publicly visible IP address of the process. Port is determined by process ID"; 75 }; 76 77 listenAddress = mkOption { 78 type = types.str; 79 default = "public"; 80 description = "Publicly visible IP address of the process. Port is determined by process ID"; 81 }; 82 83 listenPortStart = mkOption { 84 type = types.int; 85 default = 4500; 86 description = '' 87 Starting port number for database listening sockets. Every FDB process binds to a 88 subsequent port, to this number reflects the start of the overall range. e.g. having 89 8 server processes will use all ports between 4500 and 4507. 90 ''; 91 }; 92 93 openFirewall = mkOption { 94 type = types.bool; 95 default = false; 96 description = '' 97 Open the firewall ports corresponding to FoundationDB processes and coordinators 98 using <option>config.networking.firewall.*</option>. 99 ''; 100 }; 101 102 dataDir = mkOption { 103 type = types.path; 104 default = "/var/lib/foundationdb"; 105 description = "Data directory. All cluster data will be put under here."; 106 }; 107 108 logDir = mkOption { 109 type = types.path; 110 default = "/var/log/foundationdb"; 111 description = "Log directory."; 112 }; 113 114 user = mkOption { 115 type = types.str; 116 default = "foundationdb"; 117 description = "User account under which FoundationDB runs."; 118 }; 119 120 group = mkOption { 121 type = types.str; 122 default = "foundationdb"; 123 description = "Group account under which FoundationDB runs."; 124 }; 125 126 class = mkOption { 127 type = types.nullOr (types.enum [ "storage" "transaction" "stateless" ]); 128 default = null; 129 description = "Process class"; 130 }; 131 132 restartDelay = mkOption { 133 type = types.int; 134 default = 10; 135 description = "Number of seconds to wait before restarting servers."; 136 }; 137 138 logSize = mkOption { 139 type = types.string; 140 default = "10MiB"; 141 description = '' 142 Roll over to a new log file after the current log file 143 reaches the specified size. 144 ''; 145 }; 146 147 maxLogSize = mkOption { 148 type = types.string; 149 default = "100MiB"; 150 description = '' 151 Delete the oldest log file when the total size of all log 152 files exceeds the specified size. If set to 0, old log files 153 will not be deleted. 154 ''; 155 }; 156 157 serverProcesses = mkOption { 158 type = types.int; 159 default = 1; 160 description = "Number of fdbserver processes to run."; 161 }; 162 163 backupProcesses = mkOption { 164 type = types.int; 165 default = 1; 166 description = "Number of backup_agent processes to run for snapshots."; 167 }; 168 169 memory = mkOption { 170 type = types.string; 171 default = "8GiB"; 172 description = '' 173 Maximum memory used by the process. The default value is 174 <literal>8GiB</literal>. When specified without a unit, 175 <literal>MiB</literal> is assumed. This parameter does not 176 change the memory allocation of the program. Rather, it sets 177 a hard limit beyond which the process will kill itself and 178 be restarted. The default value of <literal>8GiB</literal> 179 is double the intended memory usage in the default 180 configuration (providing an emergency buffer to deal with 181 memory leaks or similar problems). It is not recommended to 182 decrease the value of this parameter below its default 183 value. It may be increased if you wish to allocate a very 184 large amount of storage engine memory or cache. In 185 particular, when the <literal>storageMemory</literal> 186 parameter is increased, the <literal>memory</literal> 187 parameter should be increased by an equal amount. 188 ''; 189 }; 190 191 storageMemory = mkOption { 192 type = types.string; 193 default = "1GiB"; 194 description = '' 195 Maximum memory used for data storage. The default value is 196 <literal>1GiB</literal>. When specified without a unit, 197 <literal>MB</literal> is assumed. Clusters using the memory 198 storage engine will be restricted to using this amount of 199 memory per process for purposes of data storage. Memory 200 overhead associated with storing the data is counted against 201 this total. If you increase the 202 <literal>storageMemory</literal>, you should also increase 203 the <literal>memory</literal> parameter by the same amount. 204 ''; 205 }; 206 207 tls = mkOption { 208 default = null; 209 description = '' 210 FoundationDB Transport Security Layer (TLS) settings. 211 ''; 212 213 type = types.nullOr (types.submodule ({ 214 options = { 215 certificate = mkOption { 216 type = types.str; 217 description = '' 218 Path to the TLS certificate file. This certificate will 219 be offered to, and may be verified by, clients. 220 ''; 221 }; 222 223 key = mkOption { 224 type = types.str; 225 description = "Private key file for the certificate."; 226 }; 227 228 allowedPeers = mkOption { 229 type = types.str; 230 default = "Check.Valid=1,Check.Unexpired=1"; 231 description = '' 232 "Peer verification string". This may be used to adjust which TLS 233 client certificates a server will accept, as a form of user 234 authorization; for example, it may only accept TLS clients who 235 offer a certificate abiding by some locality or organization name. 236 237 For more information, please see the FoundationDB documentation. 238 ''; 239 }; 240 }; 241 })); 242 }; 243 244 locality = mkOption { 245 default = { 246 machineId = null; 247 zoneId = null; 248 datacenterId = null; 249 dataHall = null; 250 }; 251 252 description = '' 253 FoundationDB locality settings. 254 ''; 255 256 type = types.submodule ({ 257 options = { 258 machineId = mkOption { 259 default = null; 260 type = types.nullOr types.str; 261 description = '' 262 Machine identifier key. All processes on a machine should share a 263 unique id. By default, processes on a machine determine a unique id to share. 264 This does not generally need to be set. 265 ''; 266 }; 267 268 zoneId = mkOption { 269 default = null; 270 type = types.nullOr types.str; 271 description = '' 272 Zone identifier key. Processes that share a zone id are 273 considered non-unique for the purposes of data replication. 274 If unset, defaults to machine id. 275 ''; 276 }; 277 278 datacenterId = mkOption { 279 default = null; 280 type = types.nullOr types.str; 281 description = '' 282 Data center identifier key. All processes physically located in a 283 data center should share the id. If you are depending on data 284 center based replication this must be set on all processes. 285 ''; 286 }; 287 288 dataHall = mkOption { 289 default = null; 290 type = types.nullOr types.str; 291 description = '' 292 Data hall identifier key. All processes physically located in a 293 data hall should share the id. If you are depending on data 294 hall based replication this must be set on all processes. 295 ''; 296 }; 297 }; 298 }); 299 }; 300 301 extraReadWritePaths = mkOption { 302 default = [ ]; 303 type = types.listOf types.path; 304 description = '' 305 An extra set of filesystem paths that FoundationDB can read to 306 and write from. By default, FoundationDB runs under a heavily 307 namespaced systemd environment without write access to most of 308 the filesystem outside of its data and log directories. By 309 adding paths to this list, the set of writeable paths will be 310 expanded. This is useful for allowing e.g. backups to local files, 311 which must be performed on behalf of the foundationdb service. 312 ''; 313 }; 314 315 pidfile = mkOption { 316 type = types.path; 317 default = "/run/foundationdb.pid"; 318 description = "Path to pidfile for fdbmonitor."; 319 }; 320 }; 321 322 config = mkIf cfg.enable { 323 environment.systemPackages = [ pkg ]; 324 325 users.users = optionalAttrs (cfg.user == "foundationdb") (singleton 326 { name = "foundationdb"; 327 description = "FoundationDB User"; 328 uid = config.ids.uids.foundationdb; 329 group = cfg.group; 330 }); 331 332 users.groups = optionalAttrs (cfg.group == "foundationdb") (singleton 333 { name = "foundationdb"; 334 gid = config.ids.gids.foundationdb; 335 }); 336 337 networking.firewall.allowedTCPPortRanges = mkIf cfg.openFirewall 338 [ { from = cfg.listenPortStart; 339 to = (cfg.listenPortStart + cfg.serverProcesses) - 1; 340 } 341 ]; 342 343 systemd.services.foundationdb = { 344 description = "FoundationDB Service"; 345 346 after = [ "network.target" ]; 347 wantedBy = [ "multi-user.target" ]; 348 unitConfig = 349 { RequiresMountsFor = "${cfg.dataDir} ${cfg.logDir}"; 350 }; 351 352 serviceConfig = 353 let rwpaths = [ cfg.dataDir cfg.logDir cfg.pidfile "/etc/foundationdb" ] 354 ++ cfg.extraReadWritePaths; 355 in 356 { Type = "simple"; 357 Restart = "always"; 358 RestartSec = 5; 359 User = cfg.user; 360 Group = cfg.group; 361 PIDFile = "${cfg.pidfile}"; 362 363 PermissionsStartOnly = true; # setup needs root perms 364 TimeoutSec = 120; # give reasonable time to shut down 365 366 # Security options 367 NoNewPrivileges = true; 368 ProtectHome = true; 369 ProtectSystem = "strict"; 370 ProtectKernelTunables = true; 371 ProtectControlGroups = true; 372 PrivateTmp = true; 373 PrivateDevices = true; 374 ReadWritePaths = lib.concatStringsSep " " (map (x: "-" + x) rwpaths); 375 }; 376 377 path = [ pkg pkgs.coreutils ]; 378 379 preStart = '' 380 rm -f ${cfg.pidfile} && \ 381 touch ${cfg.pidfile} && \ 382 chown -R ${cfg.user}:${cfg.group} ${cfg.pidfile} 383 384 for x in "${cfg.logDir}" "${cfg.dataDir}"; do 385 [ ! -d "$x" ] && mkdir -m 0700 -vp "$x"; 386 chown -R ${cfg.user}:${cfg.group} "$x"; 387 done 388 389 [ ! -d /etc/foundationdb ] && \ 390 mkdir -m 0775 -vp /etc/foundationdb && \ 391 chown -R ${cfg.user}:${cfg.group} "/etc/foundationdb" 392 393 if [ ! -f /etc/foundationdb/fdb.cluster ]; then 394 cf=/etc/foundationdb/fdb.cluster 395 desc=$(tr -dc A-Za-z0-9 </dev/urandom 2>/dev/null | head -c8) 396 rand=$(tr -dc A-Za-z0-9 </dev/urandom 2>/dev/null | head -c8) 397 echo ''${desc}:''${rand}@${initialIpAddr}:${builtins.toString cfg.listenPortStart} > $cf 398 chmod 0664 $cf && chown -R ${cfg.user}:${cfg.group} $cf 399 touch "${cfg.dataDir}/.first_startup" 400 fi 401 ''; 402 403 script = "exec fdbmonitor --lockfile ${cfg.pidfile} --conffile ${configFile}"; 404 405 postStart = '' 406 if [ -e "${cfg.dataDir}/.first_startup" ]; then 407 fdbcli --exec "configure new single memory" 408 rm -f "${cfg.dataDir}/.first_startup"; 409 fi 410 ''; 411 }; 412 }; 413 414 meta.doc = ./foundationdb.xml; 415 meta.maintainers = with lib.maintainers; [ thoughtpolice ]; 416}