]>
Commit | Line | Data |
---|---|---|
dfff8bd5 MM |
1 | # vi: encoding=utf-8 ts=8 sts=4 sw=4 et |
2 | ||
fe8cf8f9 JR |
3 | from __future__ import print_function |
4 | ||
3ea4b156 MM |
5 | import sys |
6 | import os | |
7 | import atexit | |
8 | import time | |
8152253f | 9 | import datetime |
deda9a51 | 10 | import string |
3458d3ee | 11 | import urllib |
10764658 | 12 | import urllib2 |
3ea4b156 MM |
13 | |
14 | from config import config, init_conf | |
15 | from bqueue import B_Queue | |
3ea4b156 MM |
16 | import lock |
17 | import util | |
3f446d8f | 18 | import loop |
3ea4b156 MM |
19 | import path |
20 | import status | |
21 | import log | |
22 | import chroot | |
23 | import ftp | |
e2cad913 | 24 | import buildlogs |
59ce7cd6 | 25 | import notify |
17f23d66 MM |
26 | import build |
27 | import report | |
73bdb36b | 28 | import install |
3ea4b156 | 29 | |
53989cf3 MM |
30 | # *HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK* |
31 | import socket | |
32 | ||
33 | socket.myorigsocket=socket.socket | |
34 | ||
35 | def mysocket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0): | |
dfff8bd5 MM |
36 | s=socket.myorigsocket(family, type, proto) |
37 | s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) | |
38 | return s | |
53989cf3 MM |
39 | |
40 | socket.socket=mysocket | |
41 | # *HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK* | |
42 | ||
3ea4b156 MM |
43 | # this code is duplicated in srpm_builder, but we |
44 | # might want to handle some cases differently here | |
45 | def pick_request(q): | |
dfff8bd5 MM |
46 | def mycmp(r1, r2): |
47 | if r1.kind != 'group' or r2.kind != 'group': | |
939af6d7 | 48 | raise Exception, "non-group requests" |
dfff8bd5 MM |
49 | pri_diff = cmp(r1.priority, r2.priority) |
50 | if pri_diff == 0: | |
51 | return cmp(r1.time, r2.time) | |
52 | else: | |
bbed4c4d | 53 | return pri_diff |
dfff8bd5 MM |
54 | q.requests.sort(mycmp) |
55 | ret = q.requests[0] | |
56 | return ret | |
3ea4b156 | 57 | |
5c91097f | 58 | def check_skip_build(r, b): |
6ce49c66 | 59 | src_url = config.control_url + "/srpms/" + r.id + "/skipme" |
79141e52 | 60 | good = False |
9143deab | 61 | b.log_line("checking if we should skip the build") |
6ce49c66 AM |
62 | while not good: |
63 | try: | |
aa90a502 AM |
64 | headers = { 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' } |
65 | req = urllib2.Request(url=src_url, headers=headers) | |
66 | f = urllib2.urlopen(req) | |
29526c54 | 67 | good = True |
f1d6a788 | 68 | except urllib2.HTTPError, error: |
70f0fc62 | 69 | return False |
10764658 AM |
70 | except urllib2.URLError, error: |
71 | # see errno.h | |
70f0fc62 ER |
72 | try: |
73 | errno = error.errno | |
74 | except AttributeError: | |
75 | # python 2.4 | |
76 | errno = error.reason[0] | |
77 | ||
78 | if errno in [-3, 60, 61, 110, 111]: | |
6ce49c66 AM |
79 | b.log_line("unable to connect... trying again") |
80 | continue | |
81 | else: | |
82 | return False | |
29526c54 | 83 | f.close() |
b8acd415 | 84 | return True |
6ce49c66 AM |
85 | return False |
86 | ||
17f23d66 | 87 | def fetch_src(r, b): |
3458d3ee | 88 | src_url = config.control_url + "/srpms/" + r.id + "/" + urllib.quote(b.src_rpm) |
dfff8bd5 MM |
89 | b.log_line("fetching %s" % src_url) |
90 | start = time.time() | |
29526c54 | 91 | good = False |
dfff8bd5 MM |
92 | while not good: |
93 | try: | |
aa90a502 AM |
94 | headers = { 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' } |
95 | req = urllib2.Request(url=src_url, headers=headers) | |
96 | f = urllib2.urlopen(req) | |
29526c54 | 97 | good = True |
f1d6a788 AM |
98 | except urllib2.HTTPError, error: |
99 | # fail in a way where cron job will retry | |
deddea65 | 100 | msg = "unable to fetch url %s, http code: %d" % (src_url, error.code) |
f1d6a788 | 101 | b.log_line(msg) |
2a7e772f AM |
102 | queue_time = time.time() - r.time |
103 | # 6 hours | |
02bb30ae | 104 | if error.code != 404 or (queue_time >= 0 and queue_time < (6 * 60 * 60)): |
2a7e772f AM |
105 | raise IOError, msg |
106 | else: | |
ec3ee349 | 107 | msg = "in queue for more than 6 hours, download failing" |
2a7e772f AM |
108 | b.log_line(msg) |
109 | return False | |
10764658 | 110 | except urllib2.URLError, error: |
27f5cb59 AM |
111 | errno = 0 |
112 | if isinstance(error.args[0], IOError): | |
113 | errno = error.args[0].errno | |
70f0fc62 ER |
114 | |
115 | if errno in [-3, 60, 61, 110, 111]: | |
deddea65 | 116 | b.log_line("unable to connect to %s... trying again" % (src_url)) |
e0226a74 | 117 | continue |
dfff8bd5 | 118 | else: |
dac46a1f | 119 | try: |
fe8cf8f9 | 120 | print("error.errno: %s" % str(error.errno)) |
dac46a1f | 121 | except Exception, e: |
fe8cf8f9 | 122 | print("error.errno: exception %s" % e) |
dac46a1f | 123 | try: |
fe8cf8f9 | 124 | print("error.reason %s" % str(error.reason)) |
dac46a1f | 125 | except Exception, e: |
fe8cf8f9 | 126 | print("error.reason exception %s" % e) |
85d03545 | 127 | raise |
dfff8bd5 MM |
128 | |
129 | o = chroot.popen("cat > %s" % b.src_rpm, mode = "w") | |
9e89ce60 AM |
130 | |
131 | try: | |
132 | bytes = util.sendfile(f, o) | |
133 | except IOError, e: | |
00a1db68 | 134 | b.log_line("error: unable to write to `%s': %s" % (b.src_rpm, e)) |
9e89ce60 AM |
135 | raise |
136 | ||
dfff8bd5 MM |
137 | f.close() |
138 | o.close() | |
139 | t = time.time() - start | |
140 | if t == 0: | |
141 | b.log_line("fetched %d bytes" % bytes) | |
142 | else: | |
143 | b.log_line("fetched %d bytes, %.1f K/s" % (bytes, bytes / 1024.0 / t)) | |
3ea4b156 | 144 | |
266325ca | 145 | def prepare_env(logfile = None): |
3cf6c853 | 146 | chroot.run(""" |
efb9c5f7 | 147 | test ! -f /proc/uptime && mount /proc 2>/dev/null |
3cf6c853 ER |
148 | test ! -c /dev/full && rm -f /dev/full && mknod -m 666 /dev/full c 1 7 |
149 | test ! -c /dev/null && rm -f /dev/null && mknod -m 666 /dev/null c 1 3 | |
150 | test ! -c /dev/random && rm -f /dev/random && mknod -m 644 /dev/random c 1 8 | |
151 | test ! -c /dev/urandom && rm -f /dev/urandom && mknod -m 644 /dev/urandom c 1 9 | |
152 | test ! -c /dev/zero && rm -f /dev/zero && mknod -m 666 /dev/zero c 1 5 | |
153 | ||
cfbb8475 | 154 | # need entry for "/" in mtab, for diskspace() to work in rpm |
4a8b119a | 155 | [ -z $(awk '$2 == "/" {print $1; exit}' /etc/mtab) ] && mount -f -t rootfs rootfs / |
cfbb8475 | 156 | |
3cf6c853 ER |
157 | # make neccessary files readable for builder user |
158 | # TODO: see if they really aren't readable for builder | |
159 | for db in Packages Name Basenames Providename Pubkeys; do | |
160 | db=/var/lib/rpm/$db | |
161 | test -f $db && chmod a+r $db | |
162 | done | |
163 | ||
164 | # try to limit network access for builder account | |
165 | /bin/setfacl -m u:builder:--- /etc/resolv.conf | |
266325ca | 166 | """, 'root', logfile = logfile) |
84c6175b | 167 | |
17f23d66 | 168 | def build_rpm(r, b): |
266325ca ER |
169 | packagename = b.get_package_name() |
170 | if not packagename: | |
4e14a9bc ER |
171 | # should not really get here |
172 | b.log_line("error: No .spec not given of malformed: '%s'" % b.spec) | |
173 | res = "FAIL_INTERNAL" | |
174 | return res | |
175 | ||
4e14a9bc | 176 | status.push("building %s (%s)" % (b.spec, packagename)) |
dfff8bd5 | 177 | b.log_line("request from: %s" % r.requester) |
6ce49c66 | 178 | |
5c91097f | 179 | if check_skip_build(r, b): |
6ce49c66 AM |
180 | b.log_line("build skipped due to src builder request") |
181 | res = "SKIP_REQUESTED" | |
182 | return res | |
183 | ||
dfff8bd5 | 184 | b.log_line("started at: %s" % time.asctime()) |
22061d14 AM |
185 | |
186 | b.log_line("killing old processes on a builder") | |
0833f373 | 187 | chroot.run("/bin/kill --verbose -9 -1", logfile = b.logfile) |
22061d14 | 188 | |
8ca13ff7 AM |
189 | b.log_line("cleaning up /tmp") |
190 | chroot.run("rm -rf /tmp/B.*", logfile = b.logfile) | |
191 | ||
dfff8bd5 MM |
192 | fetch_src(r, b) |
193 | b.log_line("installing srpm: %s" % b.src_rpm) | |
ffc633c5 | 194 | res = chroot.run(""" |
4e14a9bc | 195 | set -ex; |
266325ca | 196 | install -d %(topdir)s/{BUILD,RPMS}; |
f7d20c00 | 197 | LC_ALL=en_US.UTF-8 rpm -qp --changelog %(src_rpm)s; |
242d917b | 198 | rpm -Uhv --nodeps %(rpmdefs)s %(src_rpm)s; |
4e14a9bc ER |
199 | rm -f %(src_rpm)s; |
200 | """ % { | |
839f8b9f | 201 | 'topdir' : b.get_topdir(), |
4e14a9bc ER |
202 | 'rpmdefs' : b.rpmbuild_opts(), |
203 | 'src_rpm' : b.src_rpm | |
204 | }, logfile = b.logfile) | |
dfff8bd5 | 205 | b.files = [] |
508a95ef | 206 | |
266325ca | 207 | tmpdir = b.tmpdir() |
dfff8bd5 MM |
208 | if res: |
209 | b.log_line("error: installing src rpm failed") | |
511ece79 | 210 | res = "FAIL_SRPM_INSTALL" |
deda9a51 | 211 | else: |
84c6175b | 212 | prepare_env() |
f83b11d5 | 213 | chroot.run("set -x; install -m 700 -d %s" % tmpdir, logfile=b.logfile) |
be264f26 | 214 | b.default_target(config.arch) |
81c135f6 | 215 | # check for build arch before filling BR |
4e14a9bc | 216 | cmd = "set -ex; TMPDIR=%(tmpdir)s exec nice -n %(nice)s " \ |
266325ca | 217 | "rpmbuild -bp --short-circuit --nodeps %(rpmdefs)s --define 'prep exit 0' %(topdir)s/%(spec)s" % { |
4e14a9bc ER |
218 | 'tmpdir': tmpdir, |
219 | 'nice' : config.nice, | |
839f8b9f | 220 | 'topdir' : b.get_topdir(), |
4e14a9bc | 221 | 'rpmdefs' : b.rpmbuild_opts(), |
4e14a9bc ER |
222 | 'spec': b.spec, |
223 | } | |
81c135f6 | 224 | res = chroot.run(cmd, logfile = b.logfile) |
4f2d26fb | 225 | if res: |
511ece79 | 226 | res = "UNSUPP" |
a957e64f | 227 | b.log_line("error: build arch check (%s) failed" % cmd) |
81c135f6 ER |
228 | |
229 | if not res: | |
feb26777 | 230 | if ("no-install-br" not in r.flags) and not install.uninstall_self_conflict(b): |
511ece79 | 231 | res = "FAIL_DEPS_UNINSTALL" |
feb26777 | 232 | if ("no-install-br" not in r.flags) and not install.install_br(r, b): |
511ece79 | 233 | res = "FAIL_DEPS_INSTALL" |
73bdb36b | 234 | if not res: |
1de1342f | 235 | max_jobs = max(min(int(os.sysconf('SC_NPROCESSORS_ONLN') + 1), config.max_jobs), 1) |
85303822 | 236 | if r.max_jobs > 0: |
a1b28a50 | 237 | max_jobs = max(min(config.max_jobs, r.max_jobs), 1) |
4e14a9bc | 238 | cmd = "set -ex; : build-id: %(r_id)s; TMPDIR=%(tmpdir)s exec nice -n %(nice)s " \ |
a06220f8 | 239 | "rpmbuild -bb --define '_smp_mflags -j%(max_jobs)d' --define '_make_opts -Otarget' --define '_pld_builder 1' %(rpmdefs)s %(topdir)s/%(spec)s" % { |
4e14a9bc ER |
240 | 'r_id' : r.id, |
241 | 'tmpdir': tmpdir, | |
242 | 'nice' : config.nice, | |
243 | 'rpmdefs' : b.rpmbuild_opts(), | |
839f8b9f | 244 | 'topdir' : b.get_topdir(), |
4e14a9bc ER |
245 | 'max_jobs' : max_jobs, |
246 | 'spec': b.spec, | |
247 | } | |
81c135f6 | 248 | b.log_line("building RPM using: %s" % cmd) |
efb3621d | 249 | begin_time = time.time() |
81c135f6 | 250 | res = chroot.run(cmd, logfile = b.logfile) |
efb3621d | 251 | end_time = time.time() |
8152253f | 252 | b.log_line("ended at: %s, done in %s" % (time.asctime(), datetime.timedelta(0, end_time - begin_time))) |
4f2d26fb AM |
253 | if res: |
254 | res = "FAIL" | |
839f8b9f | 255 | files = util.collect_files(b.logfile, basedir = b.get_topdir()) |
81c135f6 ER |
256 | if len(files) > 0: |
257 | r.chroot_files.extend(files) | |
258 | else: | |
259 | b.log_line("error: No files produced.") | |
2b9e7381 AM |
260 | last_section = util.find_last_section(b.logfile) |
261 | if last_section == None: | |
262 | res = "FAIL" | |
263 | else: | |
264 | res = "FAIL_%s" % last_section.upper() | |
81c135f6 ER |
265 | b.files = files |
266 | ||
b9b4ac17 | 267 | # cleanup tmp and build files |
4e14a9bc ER |
268 | chroot.run(""" |
269 | set -ex; | |
266325ca | 270 | chmod -R u+rwX %(topdir)s/BUILD; |
b9b4ac17 | 271 | rm -rf %(topdir)s/{tmp,BUILD} |
266325ca | 272 | """ % { |
839f8b9f | 273 | 'topdir' : b.get_topdir(), |
266325ca | 274 | }, logfile = b.logfile) |
dfff8bd5 MM |
275 | |
276 | def ll(l): | |
277 | util.append_to(b.logfile, l) | |
e6376553 | 278 | |
dfff8bd5 | 279 | if b.files != []: |
fd499dc1 | 280 | rpm_cache_dir = config.rpm_cache_dir |
e936beda | 281 | if "test-build" not in r.flags: |
adc1235e | 282 | # NOTE: copying to cache dir doesn't mean that build failed, so ignore result |
90d9480e | 283 | b.log_line("copy rpm files to cache_dir: %s" % rpm_cache_dir) |
adc1235e | 284 | chroot.run( |
fd499dc1 ER |
285 | "cp -f %s %s && poldek --mo=nodiff --mkidxz -s %s/" % \ |
286 | (string.join(b.files), rpm_cache_dir, rpm_cache_dir), | |
287 | logfile = b.logfile, user = "root" | |
288 | ) | |
e936beda | 289 | else: |
fd499dc1 | 290 | ll("test-build: not copying to " + rpm_cache_dir) |
dfff8bd5 MM |
291 | ll("Begin-PLD-Builder-Info") |
292 | if "upgrade" in r.flags: | |
73bdb36b | 293 | b.upgraded = install.upgrade_from_batch(r, b) |
dfff8bd5 MM |
294 | else: |
295 | ll("not upgrading") | |
296 | ll("End-PLD-Builder-Info") | |
deda9a51 | 297 | |
1051562e | 298 | for f in b.files: |
dfff8bd5 | 299 | local = r.tmp_dir + os.path.basename(f) |
3e689257 | 300 | chroot.cp(f, outfile = local, rm = True) |
dfff8bd5 | 301 | ftp.add(local) |
1051562e | 302 | |
b9b4ac17 ER |
303 | # cleanup all remains from this build |
304 | chroot.run(""" | |
305 | set -ex; | |
306 | rm -rf %(topdir)s; | |
307 | """ % { | |
839f8b9f | 308 | 'topdir' : b.get_topdir(), |
b9b4ac17 ER |
309 | }, logfile = b.logfile) |
310 | ||
dfff8bd5 MM |
311 | def uploadinfo(b): |
312 | c="file:SRPMS:%s\n" % b.src_rpm | |
313 | for f in b.files: | |
314 | c=c + "file:ARCH:%s\n" % os.path.basename(f) | |
315 | c=c + "END\n" | |
316 | return c | |
3ea4b156 | 317 | |
71a06b82 | 318 | if config.gen_upinfo and b.files != [] and 'test-build' not in r.flags: |
dfff8bd5 MM |
319 | fname = r.tmp_dir + b.src_rpm + ".uploadinfo" |
320 | f = open(fname, "w") | |
321 | f.write(uploadinfo(b)) | |
322 | f.close() | |
323 | ftp.add(fname, "uploadinfo") | |
324 | ||
325 | status.pop() | |
326 | ||
327 | return res | |
3ea4b156 | 328 | |
17f23d66 | 329 | def handle_request(r): |
dfff8bd5 MM |
330 | ftp.init(r) |
331 | buildlogs.init(r) | |
332 | build.build_all(r, build_rpm) | |
333 | report.send_report(r, is_src = False) | |
334 | ftp.flush() | |
63319e0a | 335 | notify.send(r) |
3ea4b156 MM |
336 | |
337 | def check_load(): | |
dfff8bd5 MM |
338 | do_exit = 0 |
339 | try: | |
340 | f = open("/proc/loadavg") | |
341 | if float(string.split(f.readline())[2]) > config.max_load: | |
342 | do_exit = 1 | |
343 | except: | |
344 | pass | |
345 | if do_exit: | |
346 | sys.exit(0) | |
3ea4b156 | 347 | |
e8ee9db8 | 348 | def main_for(builder): |
b8ec18af AM |
349 | msg = "" |
350 | ||
dfff8bd5 | 351 | init_conf(builder) |
dfff8bd5 | 352 | |
dfff8bd5 MM |
353 | q = B_Queue(path.queue_file + "-" + config.builder) |
354 | q.lock(0) | |
355 | q.read() | |
356 | if q.requests == []: | |
357 | q.unlock() | |
358 | return | |
359 | req = pick_request(q) | |
7b16924c | 360 | q.unlock() |
dfff8bd5 | 361 | |
91112237 AM |
362 | # high priority tasks have priority < 0, normal tasks >= 0 |
363 | if req.priority >= 0: | |
b8ec18af AM |
364 | |
365 | # allow only one build in given builder at once | |
366 | if not lock.lock("building-rpm-for-%s" % config.builder, non_block = 1): | |
367 | return | |
368 | # don't kill server | |
369 | check_load() | |
370 | # not more then job_slots builds at once | |
371 | locked = 0 | |
372 | for slot in range(config.job_slots): | |
373 | if lock.lock("building-rpm-slot-%d" % slot, non_block = 1): | |
374 | locked = 1 | |
375 | break | |
376 | if not locked: | |
377 | return | |
378 | ||
379 | # record fact that we got lock for this builder, load balancer | |
380 | # will use it for fair-queuing | |
381 | l = lock.lock("got-lock") | |
382 | f = open(path.got_lock_file, "a") | |
383 | f.write(config.builder + "\n") | |
384 | f.close() | |
385 | l.close() | |
386 | else: | |
8310d504 AM |
387 | # be able to avoid locking with very low priority |
388 | if req.priority > -1000: | |
88a664e8 AM |
389 | # don't kill server |
390 | check_load() | |
8310d504 AM |
391 | # allow only one build in given builder at once |
392 | if not lock.lock("building-high-priority-rpm-for-%s" % config.builder, non_block = 1): | |
393 | return | |
394 | ||
b8ec18af | 395 | msg = "HIGH PRIORITY: " |
e6376553 | 396 | |
b8ec18af AM |
397 | msg += "handling request %s (%d) for %s from %s, priority %s" \ |
398 | % (req.id, req.no, config.builder, req.requester, req.priority) | |
dfff8bd5 MM |
399 | log.notice(msg) |
400 | status.push(msg) | |
401 | handle_request(req) | |
402 | status.pop() | |
403 | ||
404 | def otherreqs(r): | |
405 | if r.no==req.no: | |
406 | return False | |
407 | else: | |
408 | return True | |
409 | ||
410 | q = B_Queue(path.queue_file + "-" + config.builder) | |
411 | q.lock(0) | |
412 | q.read() | |
413 | previouslen=len(q.requests) | |
414 | q.requests=filter(otherreqs, q.requests) | |
415 | if len(q.requests)<previouslen: | |
416 | q.write() | |
417 | q.unlock() | |
e6376553 | 418 | |
e8ee9db8 | 419 | def main(): |
dfff8bd5 | 420 | if len(sys.argv) < 2: |
939af6d7 | 421 | raise Exception, "fatal: need to have builder name as first arg" |
dfff8bd5 | 422 | return main_for(sys.argv[1]) |
e6376553 | 423 | |
e8ee9db8 | 424 | if __name__ == '__main__': |
dfff8bd5 | 425 | loop.run_loop(main) |