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