gogup: 0.27.5 -> 0.27.6 (#373906)
[NixPkgs.git] / nixos / modules / services / continuous-integration / buildkite-agents.nix
blob08839fbe31d3188c746e67819b5196ec0d9edad6
1 { config, lib, pkgs, ... }:
3 let
4   cfg = config.services.buildkite-agents;
6   hooksDir = hooks:
7     let
8       mkHookEntry = name: text: ''
9         ln --symbolic ${pkgs.writeShellApplication { inherit name text; }}/bin/${name} $out/${name}
10       '';
11     in
12     pkgs.runCommand "buildkite-agent-hooks" {
13       preferLocalBuild = true;
14     } ''
15       mkdir $out
16       ${lib.concatStringsSep "\n" (lib.mapAttrsToList mkHookEntry hooks)}
17     '';
19   buildkiteOptions = { name ? "", config, ... }: {
20     options = {
21       enable = lib.mkOption {
22         default = true;
23         type = lib.types.bool;
24         description = "Whether to enable this buildkite agent";
25       };
27       package = lib.mkOption {
28         default = pkgs.buildkite-agent;
29         defaultText = lib.literalExpression "pkgs.buildkite-agent";
30         description = "Which buildkite-agent derivation to use";
31         type = lib.types.package;
32       };
34       dataDir = lib.mkOption {
35         default = "/var/lib/buildkite-agent-${name}";
36         description = "The workdir for the agent";
37         type = lib.types.str;
38       };
40       extraGroups = lib.mkOption {
41         default = [ "keys" ];
42         description = "Groups the user for this buildkite agent should belong to";
43         type = lib.types.listOf lib.types.str;
44       };
46       runtimePackages = lib.mkOption {
47         default = [ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ];
48         defaultText = lib.literalExpression "[ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]";
49         description = "Add programs to the buildkite-agent environment";
50         type = lib.types.listOf lib.types.package;
51       };
53       tokenPath = lib.mkOption {
54         type = lib.types.path;
55         description = ''
56           The token from your Buildkite "Agents" page.
58           A run-time path to the token file, which is supposed to be provisioned
59           outside of Nix store.
60         '';
61       };
63       name = lib.mkOption {
64         type = lib.types.str;
65         default = "%hostname-${name}-%n";
66         description = ''
67           The name of the agent as seen in the buildkite dashboard.
68         '';
69       };
71       tags = lib.mkOption {
72         type = lib.types.attrsOf (lib.types.either lib.types.str (lib.types.listOf lib.types.str));
73         default = { };
74         example = { queue = "default"; docker = "true"; ruby2 = "true"; };
75         description = ''
76           Tags for the agent.
77         '';
78       };
80       extraConfig = lib.mkOption {
81         type = lib.types.lines;
82         default = "";
83         example = "debug=true";
84         description = ''
85           Extra lines to be added verbatim to the configuration file.
86         '';
87       };
89       privateSshKeyPath = lib.mkOption {
90         type = lib.types.nullOr lib.types.path;
91         default = null;
92         ## maximum care is taken so that secrets (ssh keys and the CI token)
93         ## don't end up in the Nix store.
94         apply = final: if final == null then null else toString final;
96         description = ''
97           OpenSSH private key
99           A run-time path to the key file, which is supposed to be provisioned
100           outside of Nix store.
101         '';
102       };
104       hooks = lib.mkOption {
105         type = lib.types.attrsOf lib.types.lines;
106         default = { };
107         example = lib.literalExpression ''
108           {
109             environment = '''
110               export SECRET_VAR=`head -1 /run/keys/secret`
111             ''';
112           }'';
113         description = ''
114           "Agent" hooks to install.
115           See <https://buildkite.com/docs/agent/v3/hooks> for possible options.
116         '';
117       };
119       hooksPath = lib.mkOption {
120         type = lib.types.path;
121         default = hooksDir config.hooks;
122         defaultText = lib.literalMD "generated from {option}`services.buildkite-agents.<name>.hooks`";
123         description = ''
124           Path to the directory storing the hooks.
125           Consider using {option}`services.buildkite-agents.<name>.hooks.<name>`
126           instead.
127         '';
128       };
130       shell = lib.mkOption {
131         type = lib.types.str;
132         default = "${pkgs.bash}/bin/bash -e -c";
133         defaultText = lib.literalExpression ''"''${pkgs.bash}/bin/bash -e -c"'';
134         description = ''
135           Command that buildkite-agent 3 will execute when it spawns a shell.
136         '';
137       };
138     };
139   };
140   enabledAgents = lib.filterAttrs (n: v: v.enable) cfg;
141   mapAgents = function: lib.mkMerge (lib.mapAttrsToList function enabledAgents);
144   options.services.buildkite-agents = lib.mkOption {
145     type = lib.types.attrsOf (lib.types.submodule buildkiteOptions);
146     default = { };
147     description = ''
148       Attribute set of buildkite agents.
149       The attribute key is combined with the hostname and a unique integer to
150       create the final agent name. This can be overridden by setting the `name`
151       attribute.
152     '';
153   };
155   config.users.users = mapAgents (name: cfg: {
156     "buildkite-agent-${name}" = {
157       name = "buildkite-agent-${name}";
158       home = cfg.dataDir;
159       createHome = true;
160       description = "Buildkite agent user";
161       extraGroups = cfg.extraGroups;
162       isSystemUser = true;
163       group = "buildkite-agent-${name}";
164     };
165   });
166   config.users.groups = mapAgents (name: cfg: {
167     "buildkite-agent-${name}" = { };
168   });
170   config.systemd.services = mapAgents (name: cfg: {
171     "buildkite-agent-${name}" = {
172       description = "Buildkite Agent";
173       wantedBy = [ "multi-user.target" ];
174       after = [ "network.target" ];
175       path = cfg.runtimePackages ++ [ cfg.package pkgs.coreutils ];
176       environment = config.networking.proxy.envVars // {
177         HOME = cfg.dataDir;
178         NIX_REMOTE = "daemon";
179       };
181       ## NB: maximum care is taken so that secrets (ssh keys and the CI token)
182       ##     don't end up in the Nix store.
183       preStart =
184         let
185           sshDir = "${cfg.dataDir}/.ssh";
186           tagStr = name: value:
187             if lib.isList value
188             then lib.concatStringsSep "," (builtins.map (v: "${name}=${v}") value)
189             else "${name}=${value}";
190           tagsStr = lib.concatStringsSep "," (lib.mapAttrsToList tagStr cfg.tags);
191         in
192         lib.optionalString (cfg.privateSshKeyPath != null) ''
193           mkdir -m 0700 -p "${sshDir}"
194           install -m600 "${toString cfg.privateSshKeyPath}" "${sshDir}/id_rsa"
195         '' + ''
196           cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF
197           token="$(cat ${toString cfg.tokenPath})"
198           name="${cfg.name}"
199           shell="${cfg.shell}"
200           tags="${tagsStr}"
201           build-path="${cfg.dataDir}/builds"
202           hooks-path="${cfg.hooksPath}"
203           ${cfg.extraConfig}
204           EOF
205         '';
207       serviceConfig = {
208         ExecStart = "${cfg.package}/bin/buildkite-agent start --config ${cfg.dataDir}/buildkite-agent.cfg";
209         User = "buildkite-agent-${name}";
210         RestartSec = 5;
211         Restart = "on-failure";
212         TimeoutSec = 10;
213         # set a long timeout to give buildkite-agent a chance to finish current builds
214         TimeoutStopSec = "2 min";
215         KillMode = "mixed";
216       };
217     };
218   });
220   config.assertions = mapAgents (name: cfg: [{
221     assertion = cfg.hooksPath != hooksDir cfg.hooks -> cfg.hooks == { };
222     message = ''
223       Options `services.buildkite-agents.${name}.hooksPath' and
224       `services.buildkite-agents.${name}.hooks.<name>' are mutually exclusive.
225     '';
226   }]);