summary refs log tree commit diff
diff options
context:
space:
mode:
authorRobert Hensing <robert@roberthensing.nl>2020-08-06 19:21:53 +0200
committerRobert Hensing <robert@roberthensing.nl>2020-08-23 20:13:15 +0200
commit346a1b0ec608b1c926b2e8d5a959c60b04656358 (patch)
tree93226781a841c5e79ee84cd7b2f276efdc462e54
parent0149bdac53a2d86fdb148c88fd62a6ef11d550d5 (diff)
downloadnixpkgs-346a1b0ec608b1c926b2e8d5a959c60b04656358.tar
nixpkgs-346a1b0ec608b1c926b2e8d5a959c60b04656358.tar.gz
nixpkgs-346a1b0ec608b1c926b2e8d5a959c60b04656358.tar.bz2
nixpkgs-346a1b0ec608b1c926b2e8d5a959c60b04656358.tar.lz
nixpkgs-346a1b0ec608b1c926b2e8d5a959c60b04656358.tar.xz
nixpkgs-346a1b0ec608b1c926b2e8d5a959c60b04656358.tar.zst
nixpkgs-346a1b0ec608b1c926b2e8d5a959c60b04656358.zip
nixos/hercules-ci-agent: init
-rw-r--r--nixos/doc/manual/release-notes/rl-2009.xml5
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix213
-rw-r--r--nixos/modules/services/continuous-integration/hercules-ci-agent/default.nix86
4 files changed, 305 insertions, 0 deletions
diff --git a/nixos/doc/manual/release-notes/rl-2009.xml b/nixos/doc/manual/release-notes/rl-2009.xml
index 7a06c06fed1..c0083eab560 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;
     <para>
       There is a new <xref linkend="opt-security.doas.enable"/> module that provides <command>doas</command>, a lighter alternative to <command>sudo</command> with many of the same features.
     </para>
+  </listitem>
+  <listitem>
+    <para>
+      <link xlink:href="https://hercules-ci.com">Hercules CI</link> Agent is a specialized build agent for projects built with Nix. See the <link xlink:href="https://nixos.org/nixos/options.html#services.hercules-ci-agent">options</link> and <link xlink:href="https://docs.hercules-ci.com/hercules-ci/getting-started/#deploy-agent">setup</link>.
+    </para>
    </listitem>
   </itemizedlist>
 
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 08ed6e63e72..6f5eccf5121 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 00000000000..4aed493c0fb
--- /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 <literal>x86_64-linux</literal>
+          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 <literal>cluster-join-token.key</literal>.
+        '';
+        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 <<EOF
+
+          You are deploying Hercules CI Agent on a system with an incompatible
+          nix-daemon. Please
+           - either upgrade Nix to version 2.4.0 (when released),
+           - or set option services.hercules-ci-agent.patchNix = true;
+           - or set option nix.package to a build of Nix 2.3 with this patch applied:
+               https://github.com/NixOS/nix/pull/3405
+
+          The patch is required for Nix-daemon clients that expect a change in binary
+          cache contents while running, like the agent's evaluator. Without it, import
+          from derivation will fail if your cluster has more than one machine.
+          We are conservative with changes to the overall system, which is why we
+          keep changes to a minimum and why we ask for confirmation in the form of
+          services.hercules-ci-agent.patchNix = true before applying.
+
+        EOF
+          exit 1
+        fi
+      '';
+      installPhase = "touch $out";
+    };
+
+  patchedNix = lib.mkIf (!lib.versionAtLeast pkgs.nix.version "2.4.0") (
+    if lib.versionAtLeast pkgs.nix.version "2.4pre"
+    then lib.warn "Hercules CI Agent module will not patch 2.4 pre-release. Make sure it includes (equivalently) PR #3043, commit d048577909 or is no older than 2020-03-13." pkgs.nix
+    else pkgs.nix.overrideAttrs (
+      o: {
+        patches = (o.patches or []) ++ [ backportNix3398 ];
+      }
+    )
+  );
+
+  backportNix3398 = pkgs.fetchurl {
+    url = "https://raw.githubusercontent.com/hercules-ci/hercules-ci-agent/hercules-ci-agent-0.7.3/for-upstream/issue-3398-path-info-cache-ttls-backport-2.3.patch";
+    sha256 = "0jfckqjir9il2il7904yc1qyadw366y7xqzg81sp9sl3f1pw70ib";
+  };
+in
+{
+  imports = [
+    (mkRenamedOptionModule ["services" "hercules-ci-agent" "extraOptions"] ["services" "hercules-ci-agent" "settings"])
+    (mkRenamedOptionModule ["services" "hercules-ci-agent" "baseDirectory"] ["services" "hercules-ci-agent" "settings" "baseDirectory"])
+    (mkRenamedOptionModule ["services" "hercules-ci-agent" "concurrentTasks"] ["services" "hercules-ci-agent" "settings" "concurrentTasks"])
+  ];
+
+  options.services.hercules-ci-agent = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enable to run Hercules CI Agent as a system service.
+
+        <link xlink:href="https://hercules-ci.com">Hercules CI</link> is a
+        continuous integation service that is centered around Nix.
+
+        Support is available at <link xlink:href="mailto:help@hercules-ci.com">help@hercules-ci.com</link>.
+      '';
+    };
+    patchNix = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Fix Nix 2.3 cache path metadata caching behavior. Has the effect of <literal>nix.package = patch pkgs.nix;</literal>
+
+        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 <literal>agent.toml</literal> file.
+
+        Not all settings are listed as options, can be set nonetheless.
+
+        For the exhaustive list of settings, see <link xlink:href="https://docs.hercules-ci.com/hercules-ci/reference/agent-config/"/>.
+      '';
+      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 00000000000..d2e7e8e18f9
--- /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 = {};
+  };
+}