]> git.pld-linux.org Git - projects/pld-builder.new.git/blob - PLD_Builder/rpm_builder.py
- fail on builds that hang longer than 6 hours
[projects/pld-builder.new.git] / PLD_Builder / rpm_builder.py
1 # vi: encoding=utf-8 ts=8 sts=4 sw=4 et
2
3 import sys
4 import os
5 import atexit
6 import time
7 import string
8 import urllib2
9
10 from config import config, init_conf
11 from bqueue import B_Queue
12 import lock
13 import util
14 import loop
15 import path
16 import status
17 import log
18 import chroot
19 import ftp
20 import buildlogs
21 import notify
22 import build
23 import report
24 import install
25
26 # *HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*
27 import socket
28
29 socket.myorigsocket=socket.socket
30
31 def mysocket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0):
32     s=socket.myorigsocket(family, type, proto)
33     s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
34     return s
35
36 socket.socket=mysocket
37 # *HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*
38
39 # this code is duplicated in srpm_builder, but we
40 # might want to handle some cases differently here
41 def pick_request(q):
42     def mycmp(r1, r2):
43         if r1.kind != 'group' or r2.kind != 'group':
44             raise Exception, "non-group requests"
45         pri_diff = cmp(r1.priority, r2.priority)
46         if pri_diff == 0:
47             return cmp(r1.time, r2.time)
48         else:
49             return pri_diff
50     q.requests.sort(mycmp)
51     ret = q.requests[0]
52     return ret
53
54 def check_skip_build(r, b):
55     src_url = config.control_url + "/srpms/" + r.id + "/skipme"
56     good  = False
57     b.log_line("checking if we should skip the build")
58     while not good:
59         try:
60             f = urllib2.urlopen(src_url)
61             good = True
62         except urllib2.HTTPError, error:
63                 return False
64         except urllib2.URLError, error:
65             # see errno.h
66             if error.errno in [-3, 60, 61, 110, 111]:
67                 b.log_line("unable to connect... trying again")
68                 continue
69             else:
70                 return False
71         return True
72         f.close()
73     return False
74
75 def fetch_src(r, b):
76     src_url = config.control_url + "/srpms/" + r.id + "/" + b.src_rpm
77     b.log_line("fetching %s" % src_url)
78     start = time.time()
79     good = False
80     while not good:
81         try:
82             f = urllib2.urlopen(src_url)
83             good = True
84         except urllib2.HTTPError, error:
85             # fail in a way where cron job will retry
86             msg = "unable to fetch file, http code: %d" % error.code
87             b.log_line(msg)
88             queue_time = time.time() - r.time
89             # 6 hours
90             if queue_time > 0 && < (6 * 60 * 60):
91                 raise IOError, msg
92             else:
93                 msg = "too long in queue, failing"
94                 b.log_line(msg)
95                 return False
96         except urllib2.URLError, error:
97             # see errno.h
98             if error.errno in [-3, 60, 61, 110, 111]:
99                 b.log_line("unable to connect... trying again")
100                 continue
101             else:
102                 raise
103
104     o = chroot.popen("cat > %s" % b.src_rpm, mode = "w")
105
106     try:
107         bytes = util.sendfile(f, o)
108     except IOError, e:
109         b.log_line("error: unable to write to `%s': %s" % (b.src_rpm, e))
110         raise
111
112     f.close()
113     o.close()
114     t = time.time() - start
115     if t == 0:
116         b.log_line("fetched %d bytes" % bytes)
117     else:
118         b.log_line("fetched %d bytes, %.1f K/s" % (bytes, bytes / 1024.0 / t))
119
120 def prepare_env():
121     chroot.run("test ! -f /proc/uptime && mount /proc", 'root')
122     chroot.run("test ! -e /dev/full && mknod /dev/full c 1 7 && chmod 666 /dev/full", 'root')
123     chroot.run("test ! -e /dev/null && mknod /dev/null c 1 3 && chmod 666 /dev/null", 'root')
124     chroot.run("test ! -e /dev/random && mknod /dev/random c 1 8 && chmod 644 /dev/random", 'root')
125     chroot.run("test ! -e /dev/urandom && mknod /dev/urandom c 1 9 && chmod 644 /dev/urandom", 'root')
126     chroot.run("test ! -e /dev/zero && mknod /dev/zero c 1 5 && chmod 666 /dev/zero", 'root')
127     # try to limit network access for builder account
128     chroot.run("/bin/setfacl -m u:builder:--- /etc/resolv.conf", 'root')
129
130 def build_rpm(r, b):
131     status.push("building %s" % b.spec)
132     b.log_line("request from: %s" % r.requester)
133
134     if check_skip_build(r, b):
135         b.log_line("build skipped due to src builder request")
136         res = "SKIP_REQUESTED"
137         return res
138
139     b.log_line("started at: %s" % time.asctime())
140     fetch_src(r, b)
141     b.log_line("installing srpm: %s" % b.src_rpm)
142     res = chroot.run("rpm -U %s" % b.src_rpm, logfile = b.logfile)
143     chroot.run("rm -f %s" % b.src_rpm, logfile = b.logfile)
144     b.files = []
145     tmpdir = "/tmp/B." + b.b_id[0:6]
146     if res:
147         b.log_line("error: installing src rpm failed")
148         res = "FAIL_SRPM_INSTALL"
149     else:
150         prepare_env()
151         chroot.run("install -m 700 -d %s" % tmpdir)
152
153         b.default_target(config.arch)
154         rpmbuild_opt = "%s %s %s" % (b.target_string(), b.kernel_string(), b.bconds_string())
155         # check for build arch before filling BR
156         cmd = "cd rpm/SPECS; TMPDIR=%s nice -n %s rpmbuild -bp --short-circuit --nodeps --define 'prep exit 0' %s %s" % \
157             (tmpdir, config.nice, rpmbuild_opt, b.spec)
158         res = chroot.run(cmd, logfile = b.logfile)
159         if res:
160             res = "UNSUPP"
161             b.log_line("error: build arch check (%s) failed" % cmd)
162
163         if not res:
164             if ("no-install-br" not in r.flags) and not install.uninstall_self_conflict(b):
165                 res = "FAIL_DEPS_UNINSTALL"
166             if ("no-install-br" not in r.flags) and not install.install_br(r, b):
167                 res = "FAIL_DEPS_INSTALL"
168             if not res:
169                 max_jobs = max(min(int(os.sysconf('SC_NPROCESSORS_ONLN') * 1.5), config.max_jobs), 1)
170                 if r.max_jobs > 0:
171                     max_jobs = max(min(config.max_jobs, r.max_jobs), 1)
172                 cmd = "cd rpm/SPECS; TMPDIR=%s nice -n %s rpmbuild -bb --define '_smp_mflags -j%d' %s %s" % \
173                             (tmpdir, config.nice, max_jobs, rpmbuild_opt, b.spec)
174                 b.log_line("building RPM using: %s" % cmd)
175                 begin_time = time.time()
176                 res = chroot.run(cmd, logfile = b.logfile)
177                 end_time = time.time()
178                 b.log_line("ended at: %s, done in %s" % (time.asctime(), time.strftime("%Hh %Mm %Ss", time.gmtime(end_time - begin_time))))
179                 if res:
180                     res = "FAIL"
181                 files = util.collect_files(b.logfile)
182                 if len(files) > 0:
183                     r.chroot_files.extend(files)
184                 else:
185                     b.log_line("error: No files produced.")
186                     last_section = util.find_last_section(b.logfile)
187                     if last_section == None:
188                         res = "FAIL"
189                     else:
190                         res = "FAIL_%s" % last_section.upper()
191                 b.files = files
192
193     chroot.run("rm -rf %s; cd rpm/SPECS; rpmbuild --nodeps --nobuild " \
194                          "--clean --rmspec --rmsource %s" % \
195                          (tmpdir, b.spec), logfile = b.logfile)
196     chroot.run("rm -rf $HOME/rpm/BUILD/*")
197
198     def ll(l):
199         util.append_to(b.logfile, l)
200  
201     if b.files != []:
202         rpm_cache_dir = config.rpm_cache_dir
203         if "test-build" not in r.flags:
204             # NOTE: copying to cache dir doesn't mean that build failed, so ignore result
205             b.log_line("copy rpm files to cache_dir: %s" % rpm_cache_dir)
206             chroot.run(
207                     "cp -f %s %s && poldek --mo=nodiff --mkidxz -s %s/" % \
208                         (string.join(b.files), rpm_cache_dir, rpm_cache_dir),
209                      logfile = b.logfile, user = "root"
210             )
211         else:
212             ll("test-build: not copying to " + rpm_cache_dir)
213         ll("Begin-PLD-Builder-Info")
214         if "upgrade" in r.flags:
215             b.upgraded = install.upgrade_from_batch(r, b)
216         else:
217             ll("not upgrading")
218         ll("End-PLD-Builder-Info")
219
220     for f in b.files:
221         local = r.tmp_dir + os.path.basename(f)
222         chroot.cp(f, outfile = local, rm = True)
223         ftp.add(local)
224
225     def uploadinfo(b):
226         c="file:SRPMS:%s\n" % b.src_rpm
227         for f in b.files:
228             c=c + "file:ARCH:%s\n" % os.path.basename(f)
229         c=c + "END\n"
230         return c
231
232     if config.gen_upinfo and b.files != [] and 'test-build' not in r.flags:
233         fname = r.tmp_dir + b.src_rpm + ".uploadinfo"
234         f = open(fname, "w")
235         f.write(uploadinfo(b))
236         f.close()
237         ftp.add(fname, "uploadinfo")
238
239     status.pop()
240
241     return res
242
243 def handle_request(r):
244     ftp.init(r)
245     buildlogs.init(r)
246     build.build_all(r, build_rpm)
247     report.send_report(r, is_src = False)
248     ftp.flush()
249     notify.send(r)
250
251 def check_load():
252     do_exit = 0
253     try:
254         f = open("/proc/loadavg")
255         if float(string.split(f.readline())[2]) > config.max_load:
256             do_exit = 1
257     except:
258         pass
259     if do_exit:
260         sys.exit(0)
261
262 def main_for(builder):
263     msg = ""
264
265     init_conf(builder)
266
267     q = B_Queue(path.queue_file + "-" + config.builder)
268     q.lock(0)
269     q.read()
270     if q.requests == []:
271         q.unlock()
272         return
273     req = pick_request(q)
274     q.unlock()
275
276     # high priority tasks have priority < 0, normal tasks >= 0
277     if req.priority >= 0:
278
279         # allow only one build in given builder at once
280         if not lock.lock("building-rpm-for-%s" % config.builder, non_block = 1):
281             return
282         # don't kill server
283         check_load()
284         # not more then job_slots builds at once
285         locked = 0
286         for slot in range(config.job_slots):
287             if lock.lock("building-rpm-slot-%d" % slot, non_block = 1):
288                 locked = 1
289                 break
290         if not locked:
291             return
292
293         # record fact that we got lock for this builder, load balancer
294         # will use it for fair-queuing
295         l = lock.lock("got-lock")
296         f = open(path.got_lock_file, "a")
297         f.write(config.builder + "\n")
298         f.close()
299         l.close()
300     else:
301         msg = "HIGH PRIORITY: "
302     
303     msg += "handling request %s (%d) for %s from %s, priority %s" \
304             % (req.id, req.no, config.builder, req.requester, req.priority)
305     log.notice(msg)
306     status.push(msg)
307     handle_request(req)
308     status.pop()
309
310     def otherreqs(r):
311         if r.no==req.no:
312             return False
313         else:
314             return True
315
316     q = B_Queue(path.queue_file + "-" + config.builder)
317     q.lock(0)
318     q.read()
319     previouslen=len(q.requests)
320     q.requests=filter(otherreqs, q.requests)
321     if len(q.requests)<previouslen:
322         q.write()
323     q.unlock()
324     
325 def main():
326     if len(sys.argv) < 2:
327         raise Exception, "fatal: need to have builder name as first arg"
328     return main_for(sys.argv[1])
329     
330 if __name__ == '__main__':
331     loop.run_loop(main)
This page took 0.062185 seconds and 4 git commands to generate.