1# NixOS module for Buildbot Worker.
2
3{ config, lib, options, pkgs, ... }:
4
5with lib;
6
7let
8 cfg = config.services.buildbot-worker;
9 opt = options.services.buildbot-worker;
10
11 package = pkgs.python3.pkgs.toPythonModule cfg.package;
12 python = package.pythonModule;
13
14 tacFile = pkgs.writeText "aur-buildbot-worker.tac" ''
15 import os
16 from io import open
17
18 from buildbot_worker.bot import Worker
19 from twisted.application import service
20
21 basedir = '${cfg.buildbotDir}'
22
23 # note: this line is matched against to check that this is a worker
24 # directory; do not edit it.
25 application = service.Application('buildbot-worker')
26
27 master_url_split = '${cfg.masterUrl}'.split(':')
28 buildmaster_host = master_url_split[0]
29 port = int(master_url_split[1])
30 workername = '${cfg.workerUser}'
31
32 with open('${cfg.workerPassFile}', 'r', encoding='utf-8') as passwd_file:
33 passwd = passwd_file.read().strip('\r\n')
34 keepalive = ${toString cfg.keepalive}
35 umask = None
36 maxdelay = 300
37 numcpus = None
38 allow_shutdown = None
39
40 s = Worker(buildmaster_host, port, workername, passwd, basedir,
41 keepalive, umask=umask, maxdelay=maxdelay,
42 numcpus=numcpus, allow_shutdown=allow_shutdown)
43 s.setServiceParent(application)
44 '';
45
46in {
47 options = {
48 services.buildbot-worker = {
49
50 enable = mkOption {
51 type = types.bool;
52 default = false;
53 description = lib.mdDoc "Whether to enable the Buildbot Worker.";
54 };
55
56 user = mkOption {
57 default = "bbworker";
58 type = types.str;
59 description = lib.mdDoc "User the buildbot Worker should execute under.";
60 };
61
62 group = mkOption {
63 default = "bbworker";
64 type = types.str;
65 description = lib.mdDoc "Primary group of buildbot Worker user.";
66 };
67
68 extraGroups = mkOption {
69 type = types.listOf types.str;
70 default = [];
71 description = lib.mdDoc "List of extra groups that the Buildbot Worker user should be a part of.";
72 };
73
74 home = mkOption {
75 default = "/home/bbworker";
76 type = types.path;
77 description = lib.mdDoc "Buildbot home directory.";
78 };
79
80 buildbotDir = mkOption {
81 default = "${cfg.home}/worker";
82 defaultText = literalExpression ''"''${config.${opt.home}}/worker"'';
83 type = types.path;
84 description = lib.mdDoc "Specifies the Buildbot directory.";
85 };
86
87 workerUser = mkOption {
88 default = "example-worker";
89 type = types.str;
90 description = lib.mdDoc "Specifies the Buildbot Worker user.";
91 };
92
93 workerPass = mkOption {
94 default = "pass";
95 type = types.str;
96 description = lib.mdDoc "Specifies the Buildbot Worker password.";
97 };
98
99 workerPassFile = mkOption {
100 type = types.path;
101 description = lib.mdDoc "File used to store the Buildbot Worker password";
102 };
103
104 hostMessage = mkOption {
105 default = null;
106 type = types.nullOr types.str;
107 description = lib.mdDoc "Description of this worker";
108 };
109
110 adminMessage = mkOption {
111 default = null;
112 type = types.nullOr types.str;
113 description = lib.mdDoc "Name of the administrator of this worker";
114 };
115
116 masterUrl = mkOption {
117 default = "localhost:9989";
118 type = types.str;
119 description = lib.mdDoc "Specifies the Buildbot Worker connection string.";
120 };
121
122 keepalive = mkOption {
123 default = 600;
124 type = types.int;
125 description = lib.mdDoc ''
126 This is a number that indicates how frequently keepalive messages should be sent
127 from the worker to the buildmaster, expressed in seconds.
128 '';
129 };
130
131 package = mkOption {
132 type = types.package;
133 default = pkgs.buildbot-worker;
134 defaultText = literalExpression "pkgs.python3Packages.buildbot-worker";
135 description = lib.mdDoc "Package to use for buildbot worker.";
136 example = literalExpression "pkgs.python2Packages.buildbot-worker";
137 };
138
139 packages = mkOption {
140 default = with pkgs; [ git ];
141 defaultText = literalExpression "[ pkgs.git ]";
142 type = types.listOf types.package;
143 description = lib.mdDoc "Packages to add to PATH for the buildbot process.";
144 };
145 };
146 };
147
148 config = mkIf cfg.enable {
149 services.buildbot-worker.workerPassFile = mkDefault (pkgs.writeText "buildbot-worker-password" cfg.workerPass);
150
151 users.groups = optionalAttrs (cfg.group == "bbworker") {
152 bbworker = { };
153 };
154
155 users.users = optionalAttrs (cfg.user == "bbworker") {
156 bbworker = {
157 description = "Buildbot Worker User.";
158 isNormalUser = true;
159 createHome = true;
160 home = cfg.home;
161 group = cfg.group;
162 extraGroups = cfg.extraGroups;
163 useDefaultShell = true;
164 };
165 };
166
167 systemd.services.buildbot-worker = {
168 description = "Buildbot Worker.";
169 after = [ "network.target" "buildbot-master.service" ];
170 wantedBy = [ "multi-user.target" ];
171 path = cfg.packages;
172 environment.PYTHONPATH = "${python.withPackages (p: [ package ])}/${python.sitePackages}";
173
174 preStart = ''
175 mkdir -vp "${cfg.buildbotDir}/info"
176 ${optionalString (cfg.hostMessage != null) ''
177 ln -sf "${pkgs.writeText "buildbot-worker-host" cfg.hostMessage}" "${cfg.buildbotDir}/info/host"
178 ''}
179 ${optionalString (cfg.adminMessage != null) ''
180 ln -sf "${pkgs.writeText "buildbot-worker-admin" cfg.adminMessage}" "${cfg.buildbotDir}/info/admin"
181 ''}
182 '';
183
184 serviceConfig = {
185 Type = "simple";
186 User = cfg.user;
187 Group = cfg.group;
188 WorkingDirectory = cfg.home;
189
190 # NOTE: call twistd directly with stdout logging for systemd
191 ExecStart = "${python.pkgs.twisted}/bin/twistd --nodaemon --pidfile= --logfile - --python ${tacFile}";
192 };
193
194 };
195 };
196
197 meta.maintainers = with lib.maintainers; [ ];
198
199}