diff --git a/nixos/doc/manual/release-notes/rl-2009.xml b/nixos/doc/manual/release-notes/rl-2009.xml index 7a06c06fed1e..c0083eab5608 100644 --- a/nixos/doc/manual/release-notes/rl-2009.xml +++ b/nixos/doc/manual/release-notes/rl-2009.xml @@ -175,6 +175,11 @@ GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost' WITH GRANT OPTION; There is a new module that provides doas, a lighter alternative to sudo with many of the same features. + + + + Hercules CI Agent is a specialized build agent for projects built with Nix. See the options and setup. + diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 08ed6e63e724..6f5eccf51212 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -263,6 +263,7 @@ ./services/continuous-integration/buildbot/worker.nix ./services/continuous-integration/buildkite-agents.nix ./services/continuous-integration/hail.nix + ./services/continuous-integration/hercules-ci-agent/default.nix ./services/continuous-integration/hydra/default.nix ./services/continuous-integration/gitlab-runner.nix ./services/continuous-integration/gocd-agent/default.nix diff --git a/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix b/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix new file mode 100644 index 000000000000..4aed493c0fb0 --- /dev/null +++ b/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix @@ -0,0 +1,213 @@ +/* + +This file is for options that NixOS and nix-darwin have in common. + +Platform-specific code is in the respective default.nix files. + + */ + +{ config, lib, options, pkgs, ... }: + +let + inherit (lib) mkOption mkIf types filterAttrs literalExample mkRenamedOptionModule; + + cfg = + config.services.hercules-ci-agent; + + format = pkgs.formats.toml {}; + + settingsModule = { config, ... }: { + freeformType = format.type; + options = { + baseDirectory = mkOption { + type = types.path; + default = "/var/lib/hercules-ci-agent"; + description = '' + State directory (secrets, work directory, etc) for agent + ''; + }; + concurrentTasks = mkOption { + description = '' + Number of tasks to perform simultaneously, such as evaluations, derivations. + + You must have a total capacity across agents of at least 2 concurrent tasks on x86_64-linux + to allow for import from derivation. + ''; + type = types.int; + default = 4; + }; + workDirectory = mkOption { + description = '' + The directory in which temporary subdirectories are created for task state. This includes sources for Nix evaluation. + ''; + type = types.path; + default = config.baseDirectory + "/work"; + defaultText = literalExample ''baseDirectory + "/work"''; + }; + staticSecretsDirectory = mkOption { + description = '' + This is the default directory to look for statically configured secrets like cluster-join-token.key. + ''; + type = types.path; + default = config.baseDirectory + "/secrets"; + defaultText = literalExample ''baseDirectory + "/secrets"''; + }; + clusterJoinTokenPath = mkOption { + description = '' + Location of the cluster-join-token.key file. + ''; + type = types.path; + default = config.staticSecretsDirectory + "/cluster-join-token.key"; + defaultText = literalExample ''staticSecretsDirectory + "/cluster-join-token.key"''; + # internal: It's a bit too detailed to show by default in the docs, + # but useful to define explicitly to allow reuse by other modules. + internal = true; + }; + binaryCachesPath = mkOption { + description = '' + Location of the binary-caches.json file. + ''; + type = types.path; + default = config.staticSecretsDirectory + "/binary-caches.json"; + defaultText = literalExample ''staticSecretsDirectory + "/binary-caches.json"''; + # internal: It's a bit too detailed to show by default in the docs, + # but useful to define explicitly to allow reuse by other modules. + internal = true; + }; + }; + }; + + checkNix = + if !cfg.checkNix + then "" + else if lib.versionAtLeast config.nix.package.version "2.4.0" + then "" + else pkgs.stdenv.mkDerivation { + name = "hercules-ci-check-system-nix-src"; + inherit (config.nix.package) src patches; + configurePhase = ":"; + buildPhase = '' + echo "Checking in-memory pathInfoCache expiry" + if ! grep 'struct PathInfoCacheValue' src/libstore/store-api.hh >/dev/null; then + cat 1>&2 <Hercules CI is a + continuous integation service that is centered around Nix. + + Support is available at help@hercules-ci.com. + ''; + }; + patchNix = mkOption { + type = types.bool; + default = false; + description = '' + Fix Nix 2.3 cache path metadata caching behavior. Has the effect of nix.package = patch pkgs.nix; + + This option will be removed when Hercules CI Agent moves to Nix 2.4 (upcoming Nix release). + ''; + }; + checkNix = mkOption { + type = types.bool; + default = true; + description = '' + Whether to make sure that the system's Nix (nix-daemon) is compatible. + + If you set this to false, please keep up with the change log. + ''; + }; + package = mkOption { + description = '' + Package containing the bin/hercules-ci-agent executable. + ''; + type = types.package; + default = pkgs.hercules-ci-agent; + defaultText = literalExample "pkgs.hercules-ci-agent"; + }; + settings = mkOption { + description = '' + These settings are written to the agent.toml file. + + Not all settings are listed as options, can be set nonetheless. + + For the exhaustive list of settings, see . + ''; + type = types.submoduleWith { modules = [ settingsModule ]; }; + }; + + /* + Internal and/or computed values. + + These are written as options instead of let binding to allow sharing with + default.nix on both NixOS and nix-darwin. + */ + tomlFile = mkOption { + type = types.path; + internal = true; + defaultText = "generated hercules-ci-agent.toml"; + description = '' + The fully assembled config file. + ''; + }; + }; + + config = mkIf cfg.enable { + nix.extraOptions = lib.addContextFrom checkNix '' + # 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 + ''; + nix.package = mkIf cfg.patchNix patchedNix; + services.hercules-ci-agent.tomlFile = + format.generate "hercules-ci-agent.toml" cfg.settings; + }; +} diff --git a/nixos/modules/services/continuous-integration/hercules-ci-agent/default.nix b/nixos/modules/services/continuous-integration/hercules-ci-agent/default.nix new file mode 100644 index 000000000000..d2e7e8e18f94 --- /dev/null +++ b/nixos/modules/services/continuous-integration/hercules-ci-agent/default.nix @@ -0,0 +1,86 @@ +/* + +This file is for NixOS-specific options and configs. + +Code that is shared with nix-darwin goes in common.nix. + + */ + +{ pkgs, config, lib, ... }: + +let + + inherit (lib) mkIf mkDefault; + + cfg = config.services.hercules-ci-agent; + + command = "${cfg.package}/bin/hercules-ci-agent --config ${cfg.tomlFile}"; + testCommand = "${command} --test-configuration"; + +in +{ + imports = [ + ./common.nix + (lib.mkRenamedOptionModule ["services" "hercules-ci-agent" "user"] ["systemd" "services" "hercules-ci-agent" "serviceConfig" "User"]) + ]; + + config = mkIf cfg.enable { + + systemd.services.hercules-ci-agent = { + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + path = [ config.nix.package ]; + serviceConfig = { + User = "hercules-ci-agent"; + ExecStart = command; + ExecStartPre = testCommand; + Restart = "on-failure"; + RestartSec = 120; + StartLimitBurst = 30 * 1000000; # practically infinite + }; + }; + + # Changes in the secrets do not affect the unit in any way that would cause + # a restart, which is currently necessary to reload the secrets. + systemd.paths.hercules-ci-agent-restart-files = { + wantedBy = [ "hercules-ci-agent.service" ]; + pathConfig = { + Unit = "hercules-ci-agent-restarter.service"; + PathChanged = [ cfg.settings.clusterJoinTokenPath cfg.settings.binaryCachesPath ]; + }; + }; + systemd.services.hercules-ci-agent-restarter = { + serviceConfig.Type = "oneshot"; + script = '' + # Wait a bit, with the effect of bundling up file changes into a single + # run of this script and hopefully a single restart. + sleep 10 + if systemctl is-active --quiet hercules-ci-agent.service; then + if ${testCommand}; then + systemctl restart hercules-ci-agent.service + else + echo 1>&2 "WARNING: Not restarting agent because config is not valid at this time." + fi + else + echo 1>&2 "Not restarting hercules-ci-agent despite config file update, because it is not already active." + fi + ''; + }; + + # Trusted user allows simplified configuration and better performance + # when operating in a cluster. + nix.trustedUsers = [ config.systemd.services.hercules-ci-agent.serviceConfig.User ]; + services.hercules-ci-agent.settings.nixUserIsTrusted = true; + + users.users.hercules-ci-agent = { + home = cfg.settings.baseDirectory; + createHome = true; + group = "hercules-ci-agent"; + description = "Hercules CI Agent system user"; + isSystemUser = true; + }; + + users.groups.hercules-ci-agent = {}; + }; +}