annotate bitten/master.py @ 466:79be3c00ae69

Applied patch to #188 for stable/configurable names of build directories. Thanks to Allen Bierbaum for the patch.
author cmlenz
date Tue, 23 Oct 2007 15:36:06 +0000
parents 977a6c122205
children 44c2b4ac6157
rev   line source
379
0df178e07fdb Use UTF-8 as encoding of source files.
cmlenz
parents: 377
diff changeset
1 # -*- coding: utf-8 -*-
13
21aa17f97522 Initial code for build master and slave... these don't do a lot yet.
cmlenz
parents:
diff changeset
2 #
408
933105ab516b Update file headers and other stuff pointing to the old home.
cmlenz
parents: 402
diff changeset
3 # Copyright (C) 2007 Edgewall Software
933105ab516b Update file headers and other stuff pointing to the old home.
cmlenz
parents: 402
diff changeset
4 # Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de>
163
634be6cbb808 Flip the switch: Bitten is now BSD-licensed.
cmlenz
parents: 157
diff changeset
5 # All rights reserved.
13
21aa17f97522 Initial code for build master and slave... these don't do a lot yet.
cmlenz
parents:
diff changeset
6 #
163
634be6cbb808 Flip the switch: Bitten is now BSD-licensed.
cmlenz
parents: 157
diff changeset
7 # This software is licensed as described in the file COPYING, which
634be6cbb808 Flip the switch: Bitten is now BSD-licensed.
cmlenz
parents: 157
diff changeset
8 # you should have received as part of this distribution. The terms
408
933105ab516b Update file headers and other stuff pointing to the old home.
cmlenz
parents: 402
diff changeset
9 # are also available at http://bitten.edgewall.org/wiki/License.
13
21aa17f97522 Initial code for build master and slave... these don't do a lot yet.
cmlenz
parents:
diff changeset
10
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
11 """Build master implementation."""
313
90422699a594 More and improved docstrings (using epydoc format).
cmlenz
parents: 312
diff changeset
12
312
1016c3d12cbc Fix UTC timestamp handling in build master.
cmlenz
parents: 310
diff changeset
13 import calendar
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
14 import re
47
083e848088ee * Improvements to the model classes, and a couple of unit tests.
cmlenz
parents: 45
diff changeset
15 import time
18
591a5a836ecc * {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents: 15
diff changeset
16
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
17 from trac.config import BoolOption, IntOption
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
18 from trac.core import *
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
19 from trac.web import IRequestHandler, HTTPBadRequest, HTTPConflict, \
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
20 HTTPForbidden, HTTPMethodNotAllowed, HTTPNotFound, \
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
21 RequestDone
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
22
227
014bc6c29dff * Factor build queue logic into a class separate from the build master.
cmlenz
parents: 213
diff changeset
23 from bitten.model import BuildConfig, Build, BuildStep, BuildLog, Report
410
7930cdd83d13 More restructuring: got rid of the `trac_ext` subpackage, which makes no sense now that the master is also coupled to Trac.
cmlenz
parents: 408
diff changeset
24 from bitten.main import BuildSystem
227
014bc6c29dff * Factor build queue logic into a class separate from the build master.
cmlenz
parents: 213
diff changeset
25 from bitten.queue import BuildQueue
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
26 from bitten.recipe import Recipe
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
27 from bitten.util import xmlio
83
42970c14524a Perform slave/platform matching at slave registration. Use builtin {{{set}}} type on Python >= 2.4.
cmlenz
parents: 82
diff changeset
28
411
a169d2e96463 Use reStructuredText as the API documentation syntax.
cmlenz
parents: 410
diff changeset
29 __all__ = ['BuildMaster']
a169d2e96463 Use reStructuredText as the API documentation syntax.
cmlenz
parents: 410
diff changeset
30 __docformat__ = 'restructuredtext en'
a169d2e96463 Use reStructuredText as the API documentation syntax.
cmlenz
parents: 410
diff changeset
31
56
033366d81def Build slave now executes the build. Closes #10.
cmlenz
parents: 51
diff changeset
32
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
33 class BuildMaster(Component):
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
34 """BEEP listener implementation for the build master."""
13
21aa17f97522 Initial code for build master and slave... these don't do a lot yet.
cmlenz
parents:
diff changeset
35
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
36 implements(IRequestHandler)
51
5caccd7b247e Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents: 49
diff changeset
37
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
38 # Configuration options
51
5caccd7b247e Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents: 49
diff changeset
39
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
40 adjust_timestamps = BoolOption('bitten', 'adjust_timestamps', False, doc=
432
74c51f648466 Started some tests for the new admin interface.
cmlenz
parents: 426
diff changeset
41 """Whether the timestamps of builds should be adjusted to be close
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
42 to the timestamps of the corresponding changesets.""")
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
43
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
44 build_all = BoolOption('bitten', 'build_all', False, doc=
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
45 """Whether to request builds of older revisions even if a younger
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
46 revision has already been built.""")
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
47
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
48 slave_timeout = IntOption('bitten', 'slave_timeout', 3600, doc=
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
49 """The time in seconds after which a build is cancelled if the slave
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
50 does not report progress.""")
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
51
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
52 # IRequestHandler methods
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
53
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
54 def match_request(self, req):
401
a10942252ebc Use POST for submitting step results, instead of PUT.
cmlenz
parents: 392
diff changeset
55 match = re.match(r'/builds(?:/(\d+)(?:/(\w+)/([^/]+)?)?)?$',
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
56 req.path_info)
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
57 if match:
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
58 if match.group(1):
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
59 req.args['id'] = match.group(1)
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
60 req.args['collection'] = match.group(2)
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
61 req.args['member'] = match.group(3)
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
62 return True
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
63
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
64 def process_request(self, req):
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
65 req.perm.assert_permission('BUILD_EXEC')
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
66
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
67 if 'id' not in req.args:
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
68 if req.method != 'POST':
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
69 raise HTTPMethodNotAllowed('Method not allowed')
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
70 return self._process_build_creation(req)
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
71
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
72 build = Build.fetch(self.env, req.args['id'])
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
73 if not build:
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
74 raise HTTPNotFound('No such build')
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
75 config = BuildConfig.fetch(self.env, build.config)
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
76
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
77 if not req.args['collection']:
420
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
78 if req.method == 'DELETE':
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
79 return self._process_build_cancellation(req, config, build)
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
80 else:
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
81 return self._process_build_initiation(req, config, build)
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
82
401
a10942252ebc Use POST for submitting step results, instead of PUT.
cmlenz
parents: 392
diff changeset
83 if req.method != 'POST':
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
84 raise HTTPMethodNotAllowed('Method not allowed')
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
85
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
86 if req.args['collection'] == 'steps':
401
a10942252ebc Use POST for submitting step results, instead of PUT.
cmlenz
parents: 392
diff changeset
87 return self._process_build_step(req, config, build)
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
88 else:
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
89 raise HTTPNotFound('No such collection')
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
90
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
91 def _process_build_creation(self, req):
419
b72802dc0632 Fix resetting of builds when multiple slaves are building simultaneously, and implement the `slave_timeout` trac.ini option.
cmlenz
parents: 411
diff changeset
92 queue = BuildQueue(self.env, build_all=self.build_all,
b72802dc0632 Fix resetting of builds when multiple slaves are building simultaneously, and implement the `slave_timeout` trac.ini option.
cmlenz
parents: 411
diff changeset
93 timeout=self.slave_timeout)
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
94 queue.populate()
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
95
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
96 try:
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
97 elem = xmlio.parse(req.read())
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
98 except xmlio.ParseError, e:
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
99 raise HTTPBadRequest('XML parser error')
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
100
426
a08fa5ce9aff Applied patch by Sam Steele for #167.
cmlenz
parents: 420
diff changeset
101 slavename = elem.attr['name']
463
977a6c122205 Make slave names available for use in target platform rules, and added some documentation to the admin panel. Closes #190.
cmlenz
parents: 459
diff changeset
102 properties = {'name': slavename, Build.IP_ADDRESS: req.remote_addr}
426
a08fa5ce9aff Applied patch by Sam Steele for #167.
cmlenz
parents: 420
diff changeset
103 self.log.info('Build slave %r connected from %s', slavename,
a08fa5ce9aff Applied patch by Sam Steele for #167.
cmlenz
parents: 420
diff changeset
104 req.remote_addr)
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
105
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
106 for child in elem.children():
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
107 if child.name == 'platform':
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
108 properties[Build.MACHINE] = child.gettext()
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
109 properties[Build.PROCESSOR] = child.attr.get('processor')
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
110 elif child.name == 'os':
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
111 properties[Build.OS_NAME] = child.gettext()
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
112 properties[Build.OS_FAMILY] = child.attr.get('family')
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
113 properties[Build.OS_VERSION] = child.attr.get('version')
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
114 elif child.name == 'package':
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
115 for name, value in child.attr.items():
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
116 if name == 'name':
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
117 continue
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
118 properties[child.attr['name'] + '.' + name] = value
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
119
444
22d6a7da8777 Another minor improvement to logging in the build master.
cmlenz
parents: 436
diff changeset
120 self.log.debug('Build slave configuration: %r', properties)
22d6a7da8777 Another minor improvement to logging in the build master.
cmlenz
parents: 436
diff changeset
121
426
a08fa5ce9aff Applied patch by Sam Steele for #167.
cmlenz
parents: 420
diff changeset
122 build = queue.get_build_for_slave(slavename, properties)
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
123 if not build:
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
124 req.send_response(204)
420
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
125 req.write('')
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
126 raise RequestDone
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
127
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
128 req.send_response(201)
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
129 req.send_header('Content-Type', 'text/plain')
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
130 req.send_header('Location', req.abs_href.builds(build.id))
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
131 req.write('Build pending')
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
132 raise RequestDone
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
133
420
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
134 def _process_build_cancellation(self, req, config, build):
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
135 self.log.info('Build slave %r cancelled build %d', build.slave,
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
136 build.id)
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
137 build.status = Build.PENDING
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
138 build.slave = None
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
139 build.slave_info = {}
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
140 build.started = 0
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
141 db = self.env.get_db_cnx()
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
142 for step in list(BuildStep.select(self.env, build=build.id, db=db)):
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
143 step.delete(db=db)
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
144 build.update(db=db)
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
145 db.commit()
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
146
458
c9ac97df8a5e Fix build listener invocation.
cmlenz
parents: 444
diff changeset
147 for listener in BuildSystem(self.env).listeners:
c9ac97df8a5e Fix build listener invocation.
cmlenz
parents: 444
diff changeset
148 listener.build_aborted(build)
c9ac97df8a5e Fix build listener invocation.
cmlenz
parents: 444
diff changeset
149
420
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
150 req.send_response(204)
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
151 req.write('')
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
152 raise RequestDone
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
153
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
154 def _process_build_initiation(self, req, config, build):
420
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
155 self.log.info('Build slave %r initiated build %d', build.slave,
23de253435b8 Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents: 419
diff changeset
156 build.id)
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
157 build.started = int(time.time())
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
158 build.update()
277
1141027071b3 Changes to snapshot archive creation/transmission:
cmlenz
parents: 267
diff changeset
159
458
c9ac97df8a5e Fix build listener invocation.
cmlenz
parents: 444
diff changeset
160 for listener in BuildSystem(self.env).listeners:
c9ac97df8a5e Fix build listener invocation.
cmlenz
parents: 444
diff changeset
161 listener.build_started(build)
c9ac97df8a5e Fix build listener invocation.
cmlenz
parents: 444
diff changeset
162
287
6abd43d0cd8a The build slave now stores snapshot archives and the corresponding work directories in project folders of the main work folder, to keep build configurations from different projects that share the same name separate. This also requires transmitting the project name (simply the name of the environment directory) with the build initiation.
cmlenz
parents: 284
diff changeset
163 xml = xmlio.parse(config.recipe)
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
164 xml.attr['path'] = config.path
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
165 xml.attr['revision'] = build.rev
466
79be3c00ae69 Applied patch to #188 for stable/configurable names of build directories. Thanks to Allen Bierbaum for the patch.
cmlenz
parents: 463
diff changeset
166 xml.attr['config'] = config.name
79be3c00ae69 Applied patch to #188 for stable/configurable names of build directories. Thanks to Allen Bierbaum for the patch.
cmlenz
parents: 463
diff changeset
167 xml.attr['build'] = str(build.id)
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
168 body = str(xml)
374
446092a2d2fe Don't accept build results from a slave if the build has been invalidated or it's being built by another slave. Closes #100.
wbell
parents: 373
diff changeset
169
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
170 req.send_response(200)
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
171 req.send_header('Content-Type', 'application/x-bitten+xml')
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
172 req.send_header('Content-Length', str(len(body)))
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
173 req.send_header('Content-Disposition',
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
174 'attachment; filename=recipe_%s_r%s.xml' %
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
175 (config.name, build.rev))
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
176 req.write(body)
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
177 raise RequestDone
227
014bc6c29dff * Factor build queue logic into a class separate from the build master.
cmlenz
parents: 213
diff changeset
178
401
a10942252ebc Use POST for submitting step results, instead of PUT.
cmlenz
parents: 392
diff changeset
179 def _process_build_step(self, req, config, build):
a10942252ebc Use POST for submitting step results, instead of PUT.
cmlenz
parents: 392
diff changeset
180 try:
a10942252ebc Use POST for submitting step results, instead of PUT.
cmlenz
parents: 392
diff changeset
181 elem = xmlio.parse(req.read())
a10942252ebc Use POST for submitting step results, instead of PUT.
cmlenz
parents: 392
diff changeset
182 except xmlio.ParseError, e:
a10942252ebc Use POST for submitting step results, instead of PUT.
cmlenz
parents: 392
diff changeset
183 raise HTTPBadRequest('XML parser error')
a10942252ebc Use POST for submitting step results, instead of PUT.
cmlenz
parents: 392
diff changeset
184 stepname = elem.attr['step']
a10942252ebc Use POST for submitting step results, instead of PUT.
cmlenz
parents: 392
diff changeset
185
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
186 step = BuildStep.fetch(self.env, build=build.id, name=stepname)
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
187 if step:
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
188 raise HTTPConflict('Build step already exists')
253
cda723f3ac31 Provide hooks for build notification. Closes #62.
cmlenz
parents: 245
diff changeset
189
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
190 recipe = Recipe(xmlio.parse(config.recipe))
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
191 index = None
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
192 current_step = None
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
193 for num, recipe_step in enumerate(recipe):
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
194 if recipe_step.id == stepname:
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
195 index = num
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
196 current_step = recipe_step
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
197 if index is None:
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
198 raise HTTPForbidden('No such build step')
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
199 last_step = index == num
200
692924ffed80 Changes to the BDB XML report store to support transactions. Closes #47.
cmlenz
parents: 197
diff changeset
200
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
201 self.log.debug('Slave %s completed step %d (%s) with status %s',
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
202 build.slave, index, stepname, elem.attr['status'])
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
203
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
204 db = self.env.get_db_cnx()
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
205
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
206 step = BuildStep(self.env, build=build.id, name=stepname)
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
207 try:
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
208 step.started = int(_parse_iso_datetime(elem.attr['time']))
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
209 step.stopped = step.started + float(elem.attr['duration'])
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
210 except ValueError, e:
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
211 raise HTTPBadRequest(e.args[0])
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
212 if elem.attr['status'] == 'failure':
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
213 self.log.warning('Build %s step %s failed', build.id, stepname)
109
5bf22bb87915 Transmit build log and generated data back to the build master in XML format. Closes #23.
cmlenz
parents: 96
diff changeset
214 step.status = BuildStep.FAILURE
459
6537f01b4730 Clarify `last_step` logic in master.
cmlenz
parents: 458
diff changeset
215 if current_step.onerror == 'fail':
6537f01b4730 Clarify `last_step` logic in master.
cmlenz
parents: 458
diff changeset
216 last_step = True
109
5bf22bb87915 Transmit build log and generated data back to the build master in XML format. Closes #23.
cmlenz
parents: 96
diff changeset
217 else:
5bf22bb87915 Transmit build log and generated data back to the build master in XML format. Closes #23.
cmlenz
parents: 96
diff changeset
218 step.status = BuildStep.SUCCESS
277
1141027071b3 Changes to snapshot archive creation/transmission:
cmlenz
parents: 267
diff changeset
219 step.errors += [error.gettext() for error in elem.children('error')]
112
a38eabd4b6e1 * Store build logs in a structured way, for example to highlight messages on the error stream.
cmlenz
parents: 110
diff changeset
220 step.insert(db=db)
109
5bf22bb87915 Transmit build log and generated data back to the build master in XML format. Closes #23.
cmlenz
parents: 96
diff changeset
221
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
222 # Collect log messages from the request body
203
e6ddca1e5712 Huge refactoring to remove dependency on BDB XML. Report data is now stored in the Trac database (SQLite/PostgreSQL).
cmlenz
parents: 202
diff changeset
223 for idx, log_elem in enumerate(elem.children('log')):
401
a10942252ebc Use POST for submitting step results, instead of PUT.
cmlenz
parents: 392
diff changeset
224 build_log = BuildLog(self.env, build=build.id, step=stepname,
203
e6ddca1e5712 Huge refactoring to remove dependency on BDB XML. Report data is now stored in the Trac database (SQLite/PostgreSQL).
cmlenz
parents: 202
diff changeset
225 generator=log_elem.attr.get('generator'),
e6ddca1e5712 Huge refactoring to remove dependency on BDB XML. Report data is now stored in the Trac database (SQLite/PostgreSQL).
cmlenz
parents: 202
diff changeset
226 orderno=idx)
115
16d69eb6e047 Add support for XML fragments to the {{{xmlio}}} module, so that build output and reports don't need to be nested in a meaningless element (such as {{{<log type="distutils"><messages><message ...>}}}).
cmlenz
parents: 112
diff changeset
227 for message_elem in log_elem.children('message'):
16d69eb6e047 Add support for XML fragments to the {{{xmlio}}} module, so that build output and reports don't need to be nested in a meaningless element (such as {{{<log type="distutils"><messages><message ...>}}}).
cmlenz
parents: 112
diff changeset
228 build_log.messages.append((message_elem.attr['level'],
16d69eb6e047 Add support for XML fragments to the {{{xmlio}}} module, so that build output and reports don't need to be nested in a meaningless element (such as {{{<log type="distutils"><messages><message ...>}}}).
cmlenz
parents: 112
diff changeset
229 message_elem.gettext()))
112
a38eabd4b6e1 * Store build logs in a structured way, for example to highlight messages on the error stream.
cmlenz
parents: 110
diff changeset
230 build_log.insert(db=db)
109
5bf22bb87915 Transmit build log and generated data back to the build master in XML format. Closes #23.
cmlenz
parents: 96
diff changeset
231
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
232 # Collect report data from the request body
203
e6ddca1e5712 Huge refactoring to remove dependency on BDB XML. Report data is now stored in the Trac database (SQLite/PostgreSQL).
cmlenz
parents: 202
diff changeset
233 for report_elem in elem.children('report'):
401
a10942252ebc Use POST for submitting step results, instead of PUT.
cmlenz
parents: 392
diff changeset
234 report = Report(self.env, build=build.id, step=stepname,
213
25f84dd9f159 * Refactoring of build recipes, the file format has changed slightly:
cmlenz
parents: 204
diff changeset
235 category=report_elem.attr.get('category'),
25f84dd9f159 * Refactoring of build recipes, the file format has changed slightly:
cmlenz
parents: 204
diff changeset
236 generator=report_elem.attr.get('generator'))
203
e6ddca1e5712 Huge refactoring to remove dependency on BDB XML. Report data is now stored in the Trac database (SQLite/PostgreSQL).
cmlenz
parents: 202
diff changeset
237 for item_elem in report_elem.children():
e6ddca1e5712 Huge refactoring to remove dependency on BDB XML. Report data is now stored in the Trac database (SQLite/PostgreSQL).
cmlenz
parents: 202
diff changeset
238 item = {'type': item_elem.name}
e6ddca1e5712 Huge refactoring to remove dependency on BDB XML. Report data is now stored in the Trac database (SQLite/PostgreSQL).
cmlenz
parents: 202
diff changeset
239 item.update(item_elem.attr)
e6ddca1e5712 Huge refactoring to remove dependency on BDB XML. Report data is now stored in the Trac database (SQLite/PostgreSQL).
cmlenz
parents: 202
diff changeset
240 for child_elem in item_elem.children():
e6ddca1e5712 Huge refactoring to remove dependency on BDB XML. Report data is now stored in the Trac database (SQLite/PostgreSQL).
cmlenz
parents: 202
diff changeset
241 item[child_elem.name] = child_elem.gettext()
e6ddca1e5712 Huge refactoring to remove dependency on BDB XML. Report data is now stored in the Trac database (SQLite/PostgreSQL).
cmlenz
parents: 202
diff changeset
242 report.items.append(item)
e6ddca1e5712 Huge refactoring to remove dependency on BDB XML. Report data is now stored in the Trac database (SQLite/PostgreSQL).
cmlenz
parents: 202
diff changeset
243 report.insert(db=db)
116
86439c2aa6d6 Store report data in BDB XML database. Closes #31.
cmlenz
parents: 115
diff changeset
244
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
245 # If this was the last step in the recipe we mark the build as
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
246 # completed
459
6537f01b4730 Clarify `last_step` logic in master.
cmlenz
parents: 458
diff changeset
247 if last_step:
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
248 self.log.info('Slave %s completed build %d ("%s" as of [%s])',
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
249 build.slave, build.id, build.config, build.rev)
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
250 build.stopped = step.stopped
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
251
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
252 # Determine overall outcome of the build by checking the outcome
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
253 # of the individual steps against the "onerror" specification of
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
254 # each step in the recipe
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
255 for num, recipe_step in enumerate(recipe):
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
256 step = BuildStep.fetch(self.env, build.id, recipe_step.id)
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
257 if step.status == BuildStep.FAILURE:
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
258 if recipe_step.onerror != 'ignore':
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
259 build.status = Build.FAILURE
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
260 break
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
261 else:
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
262 build.status = Build.SUCCESS
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
263
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
264 build.update(db=db)
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
265
200
692924ffed80 Changes to the BDB XML report store to support transactions. Closes #47.
cmlenz
parents: 197
diff changeset
266 db.commit()
692924ffed80 Changes to the BDB XML report store to support transactions. Closes #47.
cmlenz
parents: 197
diff changeset
267
459
6537f01b4730 Clarify `last_step` logic in master.
cmlenz
parents: 458
diff changeset
268 if last_step:
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
269 for listener in BuildSystem(self.env).listeners:
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
270 listener.build_completed(build)
253
cda723f3ac31 Provide hooks for build notification. Closes #62.
cmlenz
parents: 245
diff changeset
271
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
272 body = 'Build step processed'
402
08801667f00f Switch to urllib2 in slave so that we can support basic and digest authentication.
cmlenz
parents: 401
diff changeset
273 req.send_response(201)
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
274 req.send_header('Content-Type', 'text/plain')
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
275 req.send_header('Content-Length', str(len(body)))
402
08801667f00f Switch to urllib2 in slave so that we can support basic and digest authentication.
cmlenz
parents: 401
diff changeset
276 req.send_header('Location', req.abs_href.builds(build.id, 'steps',
08801667f00f Switch to urllib2 in slave so that we can support basic and digest authentication.
cmlenz
parents: 401
diff changeset
277 stepname))
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
278 req.write(body)
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
279 raise RequestDone
109
5bf22bb87915 Transmit build log and generated data back to the build master in XML format. Closes #23.
cmlenz
parents: 96
diff changeset
280
13
21aa17f97522 Initial code for build master and slave... these don't do a lot yet.
cmlenz
parents:
diff changeset
281
82
01200c88ddb0 Include timing information in the build messages transmitted from slave to master for better accuracy.
cmlenz
parents: 80
diff changeset
282 def _parse_iso_datetime(string):
01200c88ddb0 Include timing information in the build messages transmitted from slave to master for better accuracy.
cmlenz
parents: 80
diff changeset
283 """Minimal parser for ISO date-time strings.
01200c88ddb0 Include timing information in the build messages transmitted from slave to master for better accuracy.
cmlenz
parents: 80
diff changeset
284
01200c88ddb0 Include timing information in the build messages transmitted from slave to master for better accuracy.
cmlenz
parents: 80
diff changeset
285 Return the time as floating point number. Only handles UTC timestamps
01200c88ddb0 Include timing information in the build messages transmitted from slave to master for better accuracy.
cmlenz
parents: 80
diff changeset
286 without time zone information."""
01200c88ddb0 Include timing information in the build messages transmitted from slave to master for better accuracy.
cmlenz
parents: 80
diff changeset
287 try:
01200c88ddb0 Include timing information in the build messages transmitted from slave to master for better accuracy.
cmlenz
parents: 80
diff changeset
288 string = string.split('.', 1)[0] # strip out microseconds
312
1016c3d12cbc Fix UTC timestamp handling in build master.
cmlenz
parents: 310
diff changeset
289 return calendar.timegm(time.strptime(string, '%Y-%m-%dT%H:%M:%S'))
82
01200c88ddb0 Include timing information in the build messages transmitted from slave to master for better accuracy.
cmlenz
parents: 80
diff changeset
290 except ValueError, e:
392
026d9aa41b85 Merged HTTP branch into trunk.
cmlenz
parents: 379
diff changeset
291 raise ValueError('Invalid ISO date/time %r' % string)
Copyright (C) 2012-2017 Edgewall Software