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