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