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