From 66d74f977b26143d7b0964e794d3c39a62b7d208 Mon Sep 17 00:00:00 2001 From: "Alexander V. Nikolaev" Date: Wed, 27 Mar 2024 20:14:30 +0200 Subject: [PATCH] Initial darwin-multi agent --- flake.nix | 25 +++++ internal/nix/nix-darwin/multi.nix | 167 ++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 internal/nix/nix-darwin/multi.nix diff --git a/flake.nix b/flake.nix index aae5ff99..970efa7a 100644 --- a/flake.nix +++ b/flake.nix @@ -194,6 +194,31 @@ }; }; + darwinModules.multi-agent-service = + { lib, pkgs, ...}: + { + _file = "${toString ./flake.nix}#darwinModules.multi-agent-service"; + imports = [ + agentFromFlakeModule_multi + ./internal/nix/nix-darwin/multi.nix + ]; + + # This module replaces what's provided by nix-darwin + disabledModules = [ "services/hercules-ci-agent" ]; + + options = let inherit (lib) types mkOption; in + { + services.hercules-ci-agents = + mkOption { + type = types.attrsOf ( + types.submoduleWith { + modules = [{ config.settings.labels.module = "darwin-multi-service"; }]; + } + ); + }; + }; + }; + # A nix-darwin module with more defaults set for machines that serve as agents darwinModules.agent-profile = { lib, pkgs, ... }: diff --git a/internal/nix/nix-darwin/multi.nix b/internal/nix/nix-darwin/multi.nix new file mode 100644 index 00000000..ba1be025 --- /dev/null +++ b/internal/nix/nix-darwin/multi.nix @@ -0,0 +1,167 @@ +# Module similar to (flake).agent-service or the NixOS-bundled module, but +# supports running multiple instance of the agent, each with their own files, +# user, etc. + +systemArgs@{ pkgs, config, lib, ... }: +let + inherit (lib) mkIf mkDefault types mkOption; + inherit (lib.strings) match; + topConfig = config; + literalDocBook = lib.literalDocBook or lib.literalExample; + literalExpression = lib.literalExpression or lib.literalExample; + + submodule = { config, options, name, ... }: + let + inherit (import ../settings.nix { inherit pkgs lib; }) format makeSettingsOptions; + configFile = format.generate "hercules-ci-agent${suffix}.json" config.settings; + command = "${config.package}/bin/hercules-ci-agent --config ${configFile}"; + testCommand = "${command} --test-configuration"; + suffix = if name == "" then "" else "-${name}"; + user = topConfig.users.users.${config.user}; + in + { + options = { + systemConfig = lib.mkOption { + internal = true; + type = types.unspecified; # A function from module arguments to config. + }; + package = mkOption { + description = '' + Package containing the bin/hercules-ci-agent executable. + ''; + type = types.package; + default = pkgs.hercules-ci-agent; + defaultText = literalExpression "pkgs.hercules-ci-agent"; + }; + user = mkOption { + type = types.str; + default = "_hercules-ci-agent"; + }; + group = mkOption { + type = types.str; + default = "_hercules-ci-agent"; + }; + logFile = mkOption { + type = types.str; + default = "/var/log/hci-agent${suffix}.log"; + }; + baseDirectory = mkOption { + type = types.str; + default = "/var/lib/hercules-ci-agent${if name == "" then "" else "/${name}"}"; # Almost a `suffix` logic, but yield subdir + }; + } // makeSettingsOptions { cfg = config; opt = options; }; + config = let cfg = config; in + { + settings = { + _module.args = { + packageOption = options.package; + inherit pkgs; + }; + baseDirectory = cfg.baseDirectory; + nixUserIsTrusted = true; + labels = + let + mkIfNotNull = x: mkIf (x != null) x; + in + { + darwin.label = topConfig.system.darwinLabel; + darwin.revision = topConfig.system.darwinRevision; + darwin.version = topConfig.system.darwinVersion; + darwin.nix.daemon = topConfig.nix.useDaemon; + darwin.nix.sandbox = topConfig.nix.settings.sandbox; + }; + }; + systemConfig = { config, ... }: { + launchd.daemons."hci-agent${suffix}" = { + script = "exec ${command}"; + + path = [ config.nix.package ]; + environment = { + NIX_SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; + }; + + serviceConfig.KeepAlive = true; + serviceConfig.RunAtLoad = true; + serviceConfig.StandardErrorPath = cfg.logFile; + serviceConfig.StandardOutPath = cfg.logFile; + serviceConfig.GroupName = cfg.user; + serviceConfig.UserName = cfg.group; + serviceConfig.WorkingDirectory = cfg.baseDirectory; + serviceConfig.WatchPaths = [ + cfg.settings.staticSecretsDirectory + ]; + }; + + nix.settings.trusted-users = [ cfg.user ]; + + system.activationScripts.preActivation.text = '' + touch '${cfg.logFile}' + chown ${toString user.uid}:${toString user.gid} '${cfg.logFile}' + if ! test -d ${cfg.baseDirectory}; then + mkdir -p ${cfg.baseDirectory} + chown ${toString user.uid}:${toString user.gid} ${cfg.baseDirectory} + fi + ''; + }; + }; + }; + + mergeSub = + f: lib.mkMerge (map (sub: f (sub.systemConfig systemArgs)) (lib.attrValues config.services.hercules-ci-agents)); +in +{ + options = { + services.hercules-ci-agents = lib.mkOption { + type = types.attrsOf (types.submoduleWith { + modules = [ submodule ]; + }); + default = { }; + description = '' + Multiple instances of hercules-ci-agent can be specified. + + If you specify an instance named `""`, it will behave just as the `services.hercules-ci-agent` options did. + - User: `hercules-ci-agent` + - Default base directory: `/var/lib/hercules-ci-agent` + + Otherwise: + - User: `hercules-ci-agent` (on darwin we use same username) + - Default base directory: `/var/lib/hercules-ci-agent-''${name}` + ''; + }; + }; + + config = lib.mkMerge [ + { + nix = mergeSub (c: c.nix); + launchd = mergeSub (c: c.launchd); + system = mergeSub (c: c.system); + } + { + nix.extraOptions = lib.mkIf (config.services.hercules-ci-agents != { }) '' + # A store path that was missing at first may well have finished building, + # even shortly after the previous lookup. This *also* applies to the daemon. + narinfo-cache-negative-ttl = 0 + ''; + + users.knownGroups = [ "hercules-ci-agent" "_hercules-ci-agent" ]; + users.knownUsers = [ "hercules-ci-agent" "_hercules-ci-agent" ]; + + users.users._hercules-ci-agent = { + uid = mkDefault 399; + gid = mkDefault config.users.groups._hercules-ci-agent.gid; + home = mkDefault "/var/lib/hercules-ci-agent"; + name = "_hercules-ci-agent"; + createHome = true; + shell = "/bin/bash"; + description = "System user for the Hercules CI Agent"; + }; + users.groups._hercules-ci-agent = { + gid = mkDefault 32001; + name = "_hercules-ci-agent"; + description = "System group for the Hercules CI Agent"; + }; + } + ]; + + meta.maintainers = [ ]; +}