{ config, pkgs, lib, ... }: with lib; let format = pkgs.formats.json { }; hashiServiceModule = { config, ... }: let cfg' = config; in { options = { enable = mkEnableOption "Enable HashiCorp service"; package = mkOption { type = with types; package; }; settings = mkOption { type = format.type; default = {}; }; settingsFile = mkOption { type = with types; path; default = format.generate "${cfg'.package.pname}.json" cfg'.settings; }; command = mkOption { type = with types; str; default = let switch = { "nomad" = "agent"; "vault" = "server"; "vault-bin" = "server"; "consul" = "agent"; }; in switch.${cfg'.package.pname} or ""; }; extraSettingsPaths = mkOption { type = with types; listOf path; default = []; }; extraPluginPaths = mkOption { type = with types; listOf path; default = []; }; extraArguments = mkOption { type = with types; listOf str; default = []; }; extraPackages = mkOption { type = with types; listOf package; default = with pkgs; let switch = { "nomad" = [ coreutils iproute2 iptables ]; "vault" = [ ]; "vault-bin" = [ ]; "consul" = [ ]; }; in switch.${cfg'.package.pname} or []; }; dynamic = mkOption { type = with types; nullOr package; default = null; }; }; }; cfg = config.services.hashicorp; in { options.services.hashicorp = mkOption { type = with types; attrsOf (submodule hashiServiceModule); default = {}; }; config.environment.etc = flip mapAttrs' (filterAttrs (_: v: v.enable) cfg) (name: value: nameValuePair "${name}.d/main.json" { source = value.settingsFile; } ); config.systemd.services = zipAttrsWith (const head) [ (flip mapAttrs' (filterAttrs (_: v: v.enable) cfg) (name: value: let configOpt = let switch = { "nomad" = "--config"; "consul" = "--config-file"; "vault" = "--config"; "vault-bin" = "--config"; }; in switch.${value.package.pname} or ""; in nameValuePair ("hashicorp-" + name) { description = name; wantedBy = [ "multi-user.target" ]; wants = [ "network-online.target" ]; after = [ "network-online.target" ]; path = value.extraPackages; restartIfChanged = false; serviceConfig = { ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; ExecStart = "${value.package}/bin/${value.package.meta.mainProgram or value.package.pname} ${value.command} " + (optionalString (value.package.pname != "vault" || value.command != "agent") "${configOpt}=/etc/${name}.d ") + "${concatMapStringsSep " " (v: "${configOpt}=${v}") value.extraSettingsPaths} " + "${concatMapStringsSep " " (v: "--plugin-dir=${v}/bin") value.extraPluginPaths} " + (optionalString (value.package.pname == "vault" && value.command == "agent") "${configOpt}=/etc/${name}.d/main.json ") + "${concatStringsSep " " value.extraArguments} "; KillMode = "process"; KillSignal = "SIGINT"; LimitNOFILE = 65536; LimitNPROC = "infinity"; OOMScoreAdjust = -1000; Restart = "always"; RestartSec = 2; TasksMax = "infinity"; StateDirectory = value.package.pname; }; } )) (flip mapAttrs' (filterAttrs (_: v: v.enable && v.dynamic != null) cfg) (name: value: nameValuePair ("hashicorp-${name}-dynamic") { description = name; wantedBy = [ "hashicorp-${name}.service" ]; wants = [ "network-online.target" ]; after = [ "network-online.target" ]; before = [ "hashicorp-${name}.service" ]; path = value.extraPackages; restartIfChanged = true; serviceConfig = { ExecStart = value.dynamic; RemainAfterExit = true; Type = "oneshot"; }; } )) ]; }