{ config, lib, pkgs, ... }: with lib; let cfg = config.services.hashicorp-envoy; serviceFormat = pkgs.formats.json {}; serviceFile = name: value: if value.type == "normal" then serviceFormat.generate "${name}-service.json" { service = value.service; } else serviceFormat.generate "${name}-service.json" value.service; in { options.services.hashicorp-envoy = mkOption { description = mdDoc '' ''; type = types.attrsOf (types.submodule { options = { service = mkOption { description = mdDoc '' ''; type = with types; oneOf [ serviceFormat.type (listOf serviceFormat.type) ]; }; type = mkOption { description = mdDoc '' ''; type = with types; enum [ "ingress" "terminating" "normal" ]; default = "normal"; }; environment = mkOption { description = mdDoc '' ''; type = with types; attrsOf str; default = {}; }; adminBind = mkOption { description = mdDoc '' ''; type = types.str; }; address = mkOption { description = mdDoc '' ''; type = types.str; default = "0.0.0.0:19000"; }; drainTime = mkOption { description = mdDoc '' ''; type = types.int; default = 15; }; parentShutdownTime = mkOption { description = mdDoc '' ''; type = types.int; default = 20; }; hotRestart = mkOption { description = mdDoc '' ''; type = types.bool; default = false; }; consulPackage = mkOption { description = mdDoc '' ''; type = types.package; default = pkgs.consul; }; envoyPackage = mkOption { description = mdDoc '' ''; type = types.package; default = pkgs.envoy; }; extraConsulArgs = mkOption { description = mdDoc '' ''; type = with types; listOf str; default = []; }; }; }); default = {}; }; config = { systemd.services = flip mapAttrs' cfg (name: value: nameValuePair "hashicorp-envoy-${name}" { description = name; wantedBy = [ "multi-user.target" ]; wants = [ "network-online.target" ]; after = [ "network-online.target" ]; path = [ value.envoyPackage ]; restartIfChanged = true; preStart = if value.type == "normal" then '' ${value.consulPackage}/bin/consul services register ${serviceFile name value} '' else '' ${value.consulPackage}/bin/consul config write ${serviceFile name value} ''; postStop = if value.type == "normal" then '' ${value.consulPackage}/bin/consul services deregister -id=${value.service.id} '' else '' ${value.consulPackage}/bin/consul config delete -filename ${serviceFile name value} ''; script = let startEnvoy = pkgs.writeShellScript "start_envoy_${name}.sh" '' exec ${value.consulPackage}/bin/consul connect envoy \ ${concatStringsSep " " value.extraConsulArgs} \ ${optionalString (value.type == "normal") '' -sidecar-for ${value.service.id} \ ''} \ ${optionalString (value.type == "ingress") '' -gateway=ingress \ -register \ -service ${value.service.name} \ ''} \ -admin-bind ${value.adminBind} \ -address ${value.address} \ ${optionalString value.hotRestart '' -- \ $([[ $RESTART_EPOCH == 0 ]] && printf -- "--use-dynamic-base-id --base-id-path $RUNTIME_DIRECTORY/id") \ $([[ $RESTART_EPOCH == 0 ]] || printf -- "--base-id $(cat $RUNTIME_DIRECTORY/id)") \ --restart-epoch $RESTART_EPOCH \ --drain-time-s ${toString value.drainTime} \ --parent-shutdown-time-s ${toString value.parentShutdownTime} ''} ''; in if value.hotRestart then "exec ${pkgs.python3}/bin/python ${value.envoyPackage.src}/restarter/hot-restarter.py ${startEnvoy}" else "exec ${startEnvoy}"; environment = value.environment; serviceConfig = { ExecReload = if value.hotRestart then "${pkgs.coreutils}/bin/kill -HUP $MAINPID" else null; KillMode = "control-group"; KillSignal = "SIGINT"; LimitNOFILE = 65536; LimitNPROC = "infinity"; OOMScoreAdjust = -1000; Restart = "always"; RestartSec = 2; TasksMax = "infinity"; RuntimeDirectory = name; }; } ); }; }