my nix configs for my servers and desktop
at main 24 kB view raw
1/*https://hg.sr.ht/~dermetfan/seaweedfs-nixos/browse/seaweedfs.nix?rev=tip*/ 2 3{ config, lib, pkgs, ... }: 4 5with lib; 6 7let 8 cfg = config.modules.seaweedfs; 9 10 clusterModule = cluster: { 11 options = { 12 package = mkOption { 13 type = types.package; 14 default = pkgs.seaweedfs; 15 }; 16 17 security.grpc = let 18 auth = mkOption { 19 type = with types; nullOr (submodule { 20 options = { 21 cert = mkOption { type = path; }; 22 key = mkOption { type = path; }; 23 }; 24 }); 25 default = null; 26 }; 27 in { 28 ca = mkOption { 29 type = with types; nullOr str; 30 default = null; 31 }; 32 33 master = auth; 34 volume = auth; 35 filer = auth; 36 client = auth; 37 msgBroker = auth; 38 }; 39 40 masters = mkOption { 41 type = with types; attrsOf (submodule (masterModule cluster.config)); 42 default = {}; 43 description = "SeaweedFS masters"; 44 }; 45 46 volumes = mkOption { 47 type = with types; attrsOf (submodule (volumeModule cluster.config)); 48 default = {}; 49 description = "SeaweedFS volumes"; 50 }; 51 52 filers = mkOption { 53 type = with types; attrsOf (submodule (filerModule cluster.config)); 54 default = {}; 55 description = "SeaweedFS filers"; 56 }; 57 58 webdavs = mkOption { 59 type = with types; attrsOf (submodule (webdavModule cluster.config)); 60 default = {}; 61 description = "SeaweedFS WebDAV servers"; 62 }; 63 64 instances = mkOption { 65 type = with types; attrsOf (submodule instanceModule); 66 description = "SeaweedFS instances"; 67 default = 68 mapAttrs' (name: master: nameValuePair 69 "master-${name}" 70 { 71 inherit (master) cluster configs; 72 73 command = "master"; 74 75 args = with master; 76 [ 77 "-port=${toString port}" 78 "-volumeSizeLimitMB=${toString volumeSizeLimitMB}" 79 ] ++ 80 optional (cpuprofile != "") "-cpuprofile=${cpuprofile}" ++ 81 optional (defaultReplication != null) ("-defaultReplication=${defaultReplication.code}") ++ 82 optional disableHttp "-disableHttp" ++ 83 optional (garbageThreshold != "") "-garbageThreshold=${garbageThreshold}" ++ 84 optional (ip != "") "-ip=${ip}" ++ 85 optional (master."ip.bind" != "") "-ip.bind=${master."ip.bind"}" ++ 86 optional (mdir != "") "-mdir=${mdir}" ++ 87 optional (memprofile != "") "-memprofile=${memprofile}" ++ 88 optional metrics.enable "-metrics.address=${metrics.address.text}" ++ 89 optional (metrics.intervalSeconds != null) "-metrics.intervalSeconds=${toString metrics.intervalSeconds}" ++ 90 optional (peers != []) ("-peers=" + (concatStringsSep "," (map (peer: peer.text) peers))) ++ 91 optional resumeState "-resumeState" ++ 92 optional volumePreallocate "-volumePreallocate" ++ 93 optional (whiteList != []) ("-whiteList=" + (concatStringsSep "," whiteList)); 94 } 95 ) cluster.config.masters // 96 mapAttrs' (name: volume: nameValuePair 97 "volume-${name}" 98 { 99 inherit (volume) cluster configs; 100 101 command = "volume"; 102 103 args = with volume; 104 [ 105 "-port=${toString port}" 106 "-dir=${concatStringsSep "," dir}" 107 "-fileSizeLimitMB=${toString fileSizeLimitMB}" 108 "-idleTimeout=${toString idleTimeout}" 109 "-index=${index}" 110 "-minFreeSpacePercent=${toString minFreeSpacePercent}" 111 "-preStopSeconds=${toString preStopSeconds}" 112 ] ++ 113 optional (compactionMBps != null) ("-compactionMBps=${compactionMBps}") ++ 114 optional (cpuprofile != "") "-cpuprofile=${cpuprofile}" ++ 115 optional (dataCenter != "") "-dataCenter=${dataCenter}" ++ 116 optional volume."images.fix.orientation" "-images.fix.orientation" ++ 117 optional (ip != "") "-ip=${ip}" ++ 118 optional (volume."ip.bind" != "") "-ip.bind=${volume."ip.bind"}" ++ 119 optional (max != []) "-max=${concatStringsSep "," (map toString max)}" ++ 120 optional (memprofile != "") "-memprofile=${memprofile}" ++ 121 optional (metricsPort != null) "-metricsPort=${toString metricsPort}" ++ 122 optional (mserver != []) ("-mserver=" + (concatStringsSep "," (map (mserver: mserver.text) mserver))) ++ 123 optional (volume."port.public" != null) "-port.public=${toString volume."port.public"}" ++ 124 optional pprof "-pprof" ++ 125 optional (publicUrl != "") "-publicUrl=${publicUrl}" ++ 126 optional (rack != "") "-rack=${rack}" ++ 127 optional (!volume."read.redirect") "-read.redirect=false" ++ 128 optional (whiteList != []) ("-whiteList=" + (concatStringsSep "," whiteList)); 129 130 systemdService.preStart = "mkdir -p ${concatStringsSep " " volume.dir}"; 131 } 132 ) cluster.config.volumes // 133 mapAttrs' (name: filer: nameValuePair 134 "filer-${name}" 135 { 136 inherit (filer) cluster configs; 137 138 command = "filer"; 139 140 args = with filer; 141 [ 142 "-port=${toString port}" 143 "-dirListLimit=${toString dirListLimit}" 144 "-maxMB=${toString maxMB}" 145 ] ++ 146 optional (collection != "") "-collection=${collection}" ++ 147 optional (dataCenter != "") "-dataCenter=${dataCenter}" ++ 148 optional (defaultReplicaPlacement != null) ("-defaultReplicaPlacement=${defaultReplicaPlacement.code}") ++ 149 optional disableDirListing "-disableDirListing" ++ 150 optional disableHttp "-disableHttp" ++ 151 optional encryptVolumeData "-encryptVolumeData" ++ 152 optional (ip != "") "-ip=${ip}" ++ 153 optional (filer."ip.bind" != "") "-ip.bind=${filer."ip.bind"}" ++ 154 optional (master != []) ("-master=" + (concatStringsSep "," (map (master: master.text) master))) ++ 155 optional (metricsPort != null) "-metricsPort=${toString metricsPort}" ++ 156 optional (peers != []) ("-peers=" + (concatStringsSep "," (map (peer: peer.text) peers))) ++ 157 optional (filer."port.readonly" != null) "-port.readonly=${toString filer."port.readonly"}" ++ 158 optional (rack != "") "-rack=${rack}" ++ 159 optionals s3.enable [ 160 "-s3" 161 "-s3.port=${toString filer.s3.port}" 162 ] ++ 163 optional (s3.enable && s3."cert.file" != "") "-s3.cert.file=${s3."cert.file"}" ++ 164 optional (s3.enable && s3."key.file" != "") "-s3.key.file=${s3."key.file"}" ++ 165 optional (s3.enable && s3.config != "") "-s3.config=${s3.config}" ++ 166 optional (s3.enable && s3.domainName != []) "-s3.domainName=${concatStringsSep "," s3.domainName}"; 167 168 systemdService.preStart = let 169 conf = filer.configs.filer.leveldb2 or {}; 170 in optionalString (conf ? "dir") "mkdir -p ${conf.dir}"; 171 } 172 ) cluster.config.filers // 173 mapAttrs' (name: webdav: nameValuePair 174 "webdav-${name}" 175 { 176 inherit (webdav) cluster; 177 178 command = "webdav"; 179 180 args = with webdav; 181 [ 182 "-port=${toString port}" 183 "-filer=${filer.text}" 184 "-cacheCapacityMB=${toString cacheCapacityMB}" 185 ] ++ 186 optional (collection != "") "-collection=${collection}" ++ 187 optional (cacheDir != "") "-cacheDir=${cacheDir}"; 188 } 189 ) cluster.config.webdavs; 190 }; 191 }; 192 }; 193 194 commonModule = cluster: common: { 195 options = { 196 cluster = mkOption { 197 type = types.submodule clusterModule; 198 internal = true; 199 }; 200 201 openFirewall = mkEnableOption "open the firewall"; 202 }; 203 204 config = { inherit cluster; }; 205 }; 206 207 masterModule = cluster: master: { 208 imports = [ (commonModule cluster) ]; 209 210 options = { 211 configs = mkOption { 212 type = with types; attrsOf attrs; 213 default.master.maintenance = { 214 scripts = '' 215 ec.encode -fullPercent=95 -quietFor=1h 216 ec.rebuild -force 217 ec.balance -force 218 volume.balance -force 219 volume.fix.replication 220 ''; 221 sleep_minutes = 17; 222 }; 223 }; 224 225 cpuprofile = mkOption { 226 type = types.str; 227 default = ""; 228 }; 229 230 defaultReplication = mkOption { 231 type = types.submodule replicationModule; 232 default = {}; 233 }; 234 235 disableHttp = mkEnableOption "disable HTTP requests, gRPC only"; 236 237 garbageThreshold = mkOption { 238 type = types.str; 239 default = ""; 240 }; 241 242 ip = mkOption { 243 type = types.str; 244 default = config.networking.hostName; 245 }; 246 247 "ip.bind" = mkOption { 248 type = types.str; 249 default = "0.0.0.0"; 250 }; 251 252 mdir = mkOption { 253 type = types.str; 254 default = "."; 255 }; 256 257 memprofile = mkOption { 258 type = types.str; 259 default = ""; 260 }; 261 262 metrics = { 263 enable = mkEnableOption "Prometheus"; 264 265 address = mkOption { 266 type = types.submodule ipPortModule; 267 default = {}; 268 }; 269 270 intervalSeconds = mkOption { 271 type = types.ints.unsigned; 272 default = 15; 273 }; 274 }; 275 276 peers = mkOption { 277 type = peersType; 278 default = mapAttrsIpPort master.config.cluster.masters; 279 }; 280 281 port = mkOption { 282 type = types.port; 283 default = 9333; 284 }; 285 286 resumeState = mkEnableOption "resume previous state on master server"; 287 288 volumePreallocate = mkEnableOption "preallocate disk space for volumes"; 289 290 volumeSizeLimitMB = mkOption { 291 type = types.ints.unsigned; 292 default = 30000; 293 }; 294 295 whiteList = mkOption { 296 type = with types; listOf str; 297 default = []; 298 }; 299 }; 300 }; 301 302 volumeModule = cluster: volume: { 303 imports = [ (commonModule cluster) ]; 304 305 options = { 306 configs = mkOption { 307 type = with types; attrsOf attrs; 308 default = {}; 309 }; 310 311 compactionMBps = mkOption { 312 type = with types; nullOr ints.unsigned; 313 default = null; 314 }; 315 316 cpuprofile = mkOption { 317 type = types.str; 318 default = ""; 319 }; 320 321 dataCenter = mkOption { 322 type = types.str; 323 default = ""; 324 }; 325 326 dir = mkOption { 327 type = with types; listOf str; 328 default = [ "/var/lib/seaweedfs/${cluster._module.args.name}/volume-${volume.config._module.args.name}" ]; 329 }; 330 331 fileSizeLimitMB = mkOption { 332 type = types.ints.unsigned; 333 default = 256; 334 }; 335 336 idleTimeout = mkOption{ 337 type = types.ints.unsigned; 338 default = 30; 339 }; 340 341 "images.fix.orientation" = mkEnableOption "adjustment of jpg orientation when uploading"; 342 343 index = mkOption { 344 type = types.enum [ 345 "memory" 346 "leveldb" 347 "leveldbMedium" 348 "leveldbLarge" 349 ]; 350 default = "memory"; 351 }; 352 353 ip = mkOption { 354 type = types.str; 355 default = config.networking.hostName; 356 }; 357 358 "ip.bind" = mkOption { 359 type = types.str; 360 default = "0.0.0.0"; 361 }; 362 363 max = mkOption { 364 type = with types; listOf ints.unsigned; 365 default = [ 8 ]; 366 }; 367 368 memprofile = mkOption { 369 type = types.str; 370 default = ""; 371 }; 372 373 metricsPort = mkOption { 374 type = with types; nullOr port; 375 default = null; 376 }; 377 378 minFreeSpacePercent = mkOption { 379 type = types.ints.unsigned; 380 default = 1; 381 }; 382 383 mserver = mkOption { 384 type = peersType; 385 default = mapAttrsIpPort volume.config.cluster.masters; 386 }; 387 388 port = mkOption { 389 type = types.port; 390 default = 8080; 391 }; 392 393 "port.public" = mkOption { 394 type = with types; nullOr port; 395 default = null; 396 }; 397 398 pprof = mkEnableOption "pprof http handlers. precludes -memprofile and -cpuprofile"; 399 400 preStopSeconds = mkOption { 401 type = types.int; 402 default = 10; 403 }; 404 405 publicUrl = mkOption { 406 type = types.str; 407 default = ""; 408 }; 409 410 rack = mkOption { 411 type = types.str; 412 default = ""; 413 }; 414 415 "read.redirect" = mkOption { 416 type = types.bool; 417 default = true; 418 }; 419 420 whiteList = mkOption { 421 type = with types; listOf str; 422 default = []; 423 }; 424 }; 425 }; 426 427 filerModule = cluster: filer: { 428 imports = [ (commonModule cluster) ]; 429 430 options = { 431 configs = mkOption { 432 type = with types; attrsOf attrs; 433 default.filer.leveldb2 = { 434 enabled = true; 435 dir = "/var/lib/seaweedfs/${cluster._module.args.name}/filer-${filer.config._module.args.name}/filerldb2"; 436 }; 437 }; 438 439 collection = mkOption { 440 type = types.str; 441 default = ""; 442 }; 443 444 dataCenter = mkOption { 445 type = types.str; 446 default = ""; 447 }; 448 449 defaultReplicaPlacement = mkOption { 450 type = with types; nullOr (submodule replicationModule); 451 default = null; 452 }; 453 454 dirListLimit = mkOption { 455 type = types.ints.unsigned; 456 default = 100000; 457 }; 458 459 disableDirListing = mkEnableOption "turn off directory listing"; 460 461 disableHttp = mkEnableOption "disable http request, only gRpc operations are allowed"; 462 463 encryptVolumeData = mkEnableOption "encrypt data on volume servers"; 464 465 ip = mkOption { 466 type = types.str; 467 default = config.networking.hostName; 468 }; 469 470 "ip.bind" = mkOption { 471 type = types.str; 472 default = "0.0.0.0"; 473 }; 474 475 master = mkOption { 476 type = peersType; 477 default = mapAttrsIpPort filer.config.cluster.masters; 478 }; 479 480 maxMB = mkOption { 481 type = types.ints.unsigned; 482 default = 32; 483 }; 484 485 metricsPort = mkOption { 486 type = with types; nullOr port; 487 default = null; 488 }; 489 490 peers = mkOption { 491 type = peersType; 492 default = mapAttrsIpPort filer.config.cluster.filers; 493 }; 494 495 port = mkOption { 496 type = types.port; 497 default = 8888; 498 }; 499 500 "port.readonly" = mkOption { 501 type = with types; nullOr port; 502 default = null; 503 }; 504 505 rack = mkOption { 506 type = types.str; 507 default = ""; 508 }; 509 510 s3 = { 511 enable = mkEnableOption "whether to start S3 gateway"; 512 513 "cert.file" = mkOption { 514 type = types.path; 515 default = ""; 516 }; 517 518 config = mkOption { 519 type = types.path; 520 default = ""; 521 }; 522 523 domainName = mkOption { 524 type = with types; listOf str; 525 default = []; 526 }; 527 528 "key.file" = mkOption { 529 type = types.path; 530 default = ""; 531 }; 532 533 port = mkOption { 534 type = types.port; 535 default = 8333; 536 }; 537 }; 538 }; 539 }; 540 541 webdavModule = cluster: webdav: { 542 imports = [ (commonModule cluster) ]; 543 544 options = { 545 cacheCapacityMB = mkOption { 546 type = types.int; 547 default = 1000; 548 }; 549 550 cacheDir = mkOption { 551 type = types.str; 552 default = "."; 553 }; 554 555 collection = mkOption { 556 type = types.str; 557 default = ""; 558 }; 559 560 filer = mkOption { 561 type = types.submodule ipPortModule; 562 default = { 563 ip = "127.0.0.1"; 564 port = 8888; 565 }; 566 }; 567 568 port = mkOption { 569 type = types.port; 570 default = 7333; 571 }; 572 }; 573 }; 574 575 instanceModule = instance: { 576 options = { 577 cluster = mkOption { 578 type = types.submodule clusterModule; 579 internal = true; 580 }; 581 582 command = mkOption { 583 type = types.enum [ 584 "server" 585 "master" 586 "volume" 587 "mount" 588 "filer" 589 "filer.replicate" 590 "filer.sync" 591 "s3" 592 "msgBroker" 593 "watch" 594 "webdav" 595 ]; 596 }; 597 598 logArgs = mkOption { 599 type = with types; listOf str; 600 default = []; 601 }; 602 603 args = mkOption { 604 type = with types; listOf str; 605 default = []; 606 }; 607 608 configs = mkOption { 609 type = with types; attrsOf attrs; 610 default = {}; 611 }; 612 613 package = mkOption { 614 type = types.package; 615 default = instance.config.cluster.package; 616 }; 617 618 systemdService = mkOption { 619 type = types.attrs; 620 default = {}; 621 }; 622 }; 623 624 config = { 625 logArgs = [ "-logtostderr" ]; 626 627 systemdService.path = optional (instance.config.command == "mount") pkgs.fuse; 628 }; 629 }; 630 631 replicationModule = replication: { 632 options = { 633 dataCenter = mkOption { 634 type = types.ints.between 0 9; 635 default = 0; 636 }; 637 638 rack = mkOption { 639 type = types.ints.between 0 9; 640 default = 0; 641 }; 642 643 server = mkOption { 644 type = types.ints.between 0 9; 645 default = 0; 646 }; 647 648 code = mkOption { 649 readOnly = true; 650 internal = true; 651 type = types.str; 652 default = with replication.config; "${toString dataCenter}${toString rack}${toString server}"; 653 }; 654 }; 655 }; 656 657 peersType = with types; listOf (submodule ipPortModule); 658 659 ipPortModule = ipPort: { 660 options = { 661 ip = mkOption { 662 type = types.str; 663 }; 664 665 port = mkOption { 666 type = types.port; 667 }; 668 669 text = mkOption { 670 internal = true; 671 readOnly = true; 672 type = types.str; 673 default = with ipPort.config; "${ip}:${toString port}"; 674 }; 675 }; 676 }; 677 678 mapAttrsIpPort = attrs: mapAttrsToList (name: value: { inherit (value) ip port; }) attrs; 679 680 toTOML = with generators; toINI { 681 mkKeyValue = mkKeyValueDefault { 682 mkValueString = v: 683 if isString v 684 then ( 685 if hasInfix "\n" v 686 then '' 687 """ 688 ${removeSuffix "\n" v} 689 """ 690 '' 691 else ''"${v}"'' 692 ) 693 else mkValueStringDefault {} v; 694 } "="; 695 }; 696 697 flattenAttrs = separator: attrs: let 698 /* 699 attrs = { 700 a = { 701 m1 = {}; 702 m2 = {}; 703 }; 704 b = { 705 m1 = {}; 706 }; 707 } 708 */ 709 710 /* 711 step1 = { 712 a = [ 713 { name = "a-m1"; value = {}; } 714 { name = "a-m2"; value = {}; } 715 ]; 716 b = [ 717 { name = "b-m1"; value = {}; } 718 ]; 719 }; 720 */ 721 step1 = mapAttrs (outerName: outerValues: 722 mapAttrsToList (innerName: innerValues: nameValuePair 723 "${outerName}${separator}${innerName}" 724 innerValues 725 ) outerValues 726 ) attrs; 727 728 /* 729 step2 = [ 730 [ 731 { name = "a-m1"; value = {}; } 732 { name = "a-m2"; value = {}; } 733 ] 734 [ 735 { name = "b-m1"; value = {}; } 736 ] 737 ]; 738 */ 739 step2 = mapAttrsToList (name: value: value) step1; 740 741 /* 742 step3 = [ 743 { name = "a-m1"; value = {}; } 744 { name = "a-m2"; value = {}; } 745 { name = "b-m1"; value = {}; } 746 ]; 747 */ 748 step3 = flatten step2; 749 in 750 /* 751 { 752 a-m1 = {}; 753 a-m2 = {}; 754 b-m1 = {}; 755 }; 756 */ 757 builtins.listToAttrs step3; 758in { 759 options.modules.seaweedfs = { 760 clusters = mkOption { 761 type = with types; attrsOf (submodule clusterModule); 762 default = {}; 763 description = "SeaweedFS clusters"; 764 }; 765 }; 766 767 config = { 768 systemd.services = mapAttrs' 769 (name: instance: nameValuePair "seaweedfs-${name}" instance) 770 (flattenAttrs "-" ( 771 mapAttrs (clusterName: cluster: 772 mapAttrs (instanceName: instance: with instance; recursiveUpdate systemdService rec { 773 description = "SeaweedFS ${clusterName} ${instanceName}"; 774 wants = [ "network.target" ]; 775 after = wants; 776 wantedBy = [ "multi-user.target" ]; 777 preStart = with serviceConfig; '' 778 ${ 779 let securityFile = config.environment.etc."seaweedfs/${clusterName}/security.toml"; 780 in optionalString securityFile.enable "ln -s /etc/${securityFile.target} ${WorkingDirectory}/" 781 } 782 783 # TODO replace find usage with statically known condition 784 find -L /etc/${ConfigurationDirectory} -type f -exec ln -s '{}' ${WorkingDirectory}/ \; 785 786 ${optionalString (systemdService ? preStart) systemdService.preStart} 787 ''; 788 serviceConfig = rec { 789 ExecStart = "${package}/bin/weed ${concatStringsSep " " logArgs} ${command} ${concatStringsSep " " args}"; 790 Restart = "on-failure"; 791 Type = "exec"; 792 ConfigurationDirectory = "seaweedfs/${clusterName}/${instanceName}"; 793 RuntimeDirectory = ConfigurationDirectory; 794 RuntimeDirectoryPreserve = "restart"; 795 WorkingDirectory = "/run/${RuntimeDirectory}"; 796 }; 797 }) cluster.instances 798 ) cfg.clusters 799 )); 800 801 environment.etc = 802 (mapAttrs' (name: cluster: 803 let file = "seaweedfs/${name}/security.toml"; 804 in nameValuePair file { 805 enable = config.environment.etc.${file}.text != ""; 806 text = with cluster.security.grpc; toTOML ( 807 (if ca == null then {} else { grpc.ca = ca; }) // 808 (if master == null then {} else { "grpc.master" = { inherit (master) cert key; }; }) // 809 (if volume == null then {} else { "grpc.volume" = { inherit (volume) cert key; }; }) // 810 (if filer == null then {} else { "grpc.filer" = { inherit (filer) cert key; }; }) // 811 (if client == null then {} else { "grpc.client" = { inherit (client) cert key; }; }) // 812 (if msgBroker == null then {} else { "grpc.msg_broker" = { inherit (msgBroker) cert key; }; }) 813 ); 814 } 815 ) cfg.clusters) // 816 (mapAttrs' 817 (name: config: nameValuePair 818 "seaweedfs/${name}.toml" 819 { text = toTOML config; } 820 ) 821 (flattenAttrs "/" ( 822 mapAttrs (clusterName: cluster: 823 flattenAttrs "/" ( 824 mapAttrs 825 (instanceName: instance: instance.configs) 826 cluster.instances 827 ) 828 ) cfg.clusters 829 )) 830 ); 831 832 networking.firewall.allowedTCPPorts = let 833 modulesToPorts = extraPorts: mapAttrsToList (name: module: 834 with module; 835 optionals openFirewall ( 836 [ port (port + 10000) ] ++ 837 (filter (p: p != null) (extraPorts module)) 838 ) 839 ); 840 in flatten (mapAttrsToList (clusterName: cluster: 841 modulesToPorts 842 (master: []) 843 cluster.masters ++ 844 845 modulesToPorts 846 (volume: with volume; [ metricsPort volume."port.public" ]) 847 cluster.volumes ++ 848 849 modulesToPorts 850 (filer: with filer; [ metricsPort filer."port.readonly" s3.port]) 851 cluster.filers ++ 852 853 modulesToPorts 854 (webdav: []) 855 cluster.webdavs 856 ) cfg.clusters); 857 }; 858}