Mercurial > bitten > bitten-test
annotate bitten/master.py @ 426:a08fa5ce9aff
Applied patch by Sam Steele for #167.
author | cmlenz |
---|---|
date | Sun, 12 Aug 2007 21:46:48 +0000 |
parents | 23de253435b8 |
children | 74c51f648466 |
rev | line source |
---|---|
379 | 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 | 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 | 7 # This software is licensed as described in the file COPYING, which |
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 | 11 """Build master implementation.""" |
313 | 12 |
312 | 13 import calendar |
95
1984b2e01998
Make the repository poll interval configurable. It now defaults to 2 minutes instead of 10 seconds.
cmlenz
parents:
94
diff
changeset
|
14 from datetime import datetime, timedelta |
15
06207499c58c
* Use logging in the BEEP core as well as in the master and slave scripts. Closes #4.
cmlenz
parents:
14
diff
changeset
|
15 import logging |
227
014bc6c29dff
* Factor build queue logic into a class separate from the build master.
cmlenz
parents:
213
diff
changeset
|
16 import os |
392 | 17 import re |
83
42970c14524a
Perform slave/platform matching at slave registration. Use builtin {{{set}}} type on Python >= 2.4.
cmlenz
parents:
82
diff
changeset
|
18 try: |
42970c14524a
Perform slave/platform matching at slave registration. Use builtin {{{set}}} type on Python >= 2.4.
cmlenz
parents:
82
diff
changeset
|
19 set |
42970c14524a
Perform slave/platform matching at slave registration. Use builtin {{{set}}} type on Python >= 2.4.
cmlenz
parents:
82
diff
changeset
|
20 except NameError: |
42970c14524a
Perform slave/platform matching at slave registration. Use builtin {{{set}}} type on Python >= 2.4.
cmlenz
parents:
82
diff
changeset
|
21 from sets import Set as set |
227
014bc6c29dff
* Factor build queue logic into a class separate from the build master.
cmlenz
parents:
213
diff
changeset
|
22 import sys |
47
083e848088ee
* Improvements to the model classes, and a couple of unit tests.
cmlenz
parents:
45
diff
changeset
|
23 import time |
18
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
24 |
392 | 25 from trac.config import BoolOption, IntOption |
26 from trac.core import * | |
18
591a5a836ecc
* {{{beep.Listener}}} now has an event loop (based on code mostly from medusa)
cmlenz
parents:
15
diff
changeset
|
27 from trac.env import Environment |
392 | 28 from trac.web import IRequestHandler, HTTPBadRequest, HTTPConflict, \ |
29 HTTPForbidden, HTTPMethodNotAllowed, HTTPNotFound, \ | |
30 RequestDone | |
31 | |
227
014bc6c29dff
* Factor build queue logic into a class separate from the build master.
cmlenz
parents:
213
diff
changeset
|
32 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
|
33 from bitten.main import BuildSystem |
227
014bc6c29dff
* Factor build queue logic into a class separate from the build master.
cmlenz
parents:
213
diff
changeset
|
34 from bitten.queue import BuildQueue |
392 | 35 from bitten.recipe import Recipe |
36 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
|
37 |
411
a169d2e96463
Use reStructuredText as the API documentation syntax.
cmlenz
parents:
410
diff
changeset
|
38 __all__ = ['BuildMaster'] |
a169d2e96463
Use reStructuredText as the API documentation syntax.
cmlenz
parents:
410
diff
changeset
|
39 __docformat__ = 'restructuredtext en' |
a169d2e96463
Use reStructuredText as the API documentation syntax.
cmlenz
parents:
410
diff
changeset
|
40 |
56 | 41 |
392 | 42 class BuildMaster(Component): |
43 """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
|
44 |
392 | 45 implements(IRequestHandler) |
51
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
46 |
392 | 47 # Configuration options |
51
5caccd7b247e
Proper archive format negotiation; improved representation of parsed XML content in {{{bitten.util.xmlio}}}.
cmlenz
parents:
49
diff
changeset
|
48 |
392 | 49 adjust_timestamps = BoolOption('bitten', 'adjust_timestamps', False, doc= |
50 """Whether the timestamps of builds should be adjusted to be close ' | |
51 to the timestamps of the corresponding changesets.""") | |
52 | |
53 build_all = BoolOption('bitten', 'build_all', False, doc= | |
54 """Whether to request builds of older revisions even if a younger | |
55 revision has already been built.""") | |
56 | |
57 slave_timeout = IntOption('bitten', 'slave_timeout', 3600, doc= | |
58 """The time in seconds after which a build is cancelled if the slave | |
59 does not report progress.""") | |
60 | |
61 # IRequestHandler methods | |
62 | |
63 def match_request(self, req): | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
64 match = re.match(r'/builds(?:/(\d+)(?:/(\w+)/([^/]+)?)?)?$', |
392 | 65 req.path_info) |
66 if match: | |
67 if match.group(1): | |
68 req.args['id'] = match.group(1) | |
69 req.args['collection'] = match.group(2) | |
70 req.args['member'] = match.group(3) | |
71 return True | |
72 | |
73 def process_request(self, req): | |
74 req.perm.assert_permission('BUILD_EXEC') | |
75 | |
76 if 'id' not in req.args: | |
77 if req.method != 'POST': | |
78 raise HTTPMethodNotAllowed('Method not allowed') | |
79 return self._process_build_creation(req) | |
80 | |
81 build = Build.fetch(self.env, req.args['id']) | |
82 if not build: | |
83 raise HTTPNotFound('No such build') | |
84 config = BuildConfig.fetch(self.env, build.config) | |
85 | |
86 if not req.args['collection']: | |
420
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
87 if req.method == 'DELETE': |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
88 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
|
89 else: |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
90 return self._process_build_initiation(req, config, build) |
392 | 91 |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
92 if req.method != 'POST': |
392 | 93 raise HTTPMethodNotAllowed('Method not allowed') |
94 | |
95 if req.args['collection'] == 'steps': | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
96 return self._process_build_step(req, config, build) |
392 | 97 else: |
98 raise HTTPNotFound('No such collection') | |
99 | |
100 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
|
101 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
|
102 timeout=self.slave_timeout) |
392 | 103 queue.populate() |
104 | |
105 try: | |
106 elem = xmlio.parse(req.read()) | |
107 except xmlio.ParseError, e: | |
108 raise HTTPBadRequest('XML parser error') | |
109 | |
426 | 110 slavename = elem.attr['name'] |
392 | 111 properties = {Build.IP_ADDRESS: req.remote_addr} |
426 | 112 self.log.info('Build slave %r connected from %s', slavename, |
113 req.remote_addr) | |
392 | 114 |
115 for child in elem.children(): | |
116 if child.name == 'platform': | |
117 properties[Build.MACHINE] = child.gettext() | |
118 properties[Build.PROCESSOR] = child.attr.get('processor') | |
119 elif child.name == 'os': | |
120 properties[Build.OS_NAME] = child.gettext() | |
121 properties[Build.OS_FAMILY] = child.attr.get('family') | |
122 properties[Build.OS_VERSION] = child.attr.get('version') | |
123 elif child.name == 'package': | |
124 for name, value in child.attr.items(): | |
125 if name == 'name': | |
126 continue | |
127 properties[child.attr['name'] + '.' + name] = value | |
128 | |
426 | 129 build = queue.get_build_for_slave(slavename, properties) |
392 | 130 if not build: |
131 req.send_response(204) | |
420
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
132 req.write('') |
392 | 133 raise RequestDone |
134 | |
135 req.send_response(201) | |
136 req.send_header('Content-Type', 'text/plain') | |
137 req.send_header('Location', req.abs_href.builds(build.id)) | |
138 req.write('Build pending') | |
139 raise RequestDone | |
140 | |
420
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
141 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
|
142 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
|
143 build.id) |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
144 build.status = Build.PENDING |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
145 build.slave = None |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
146 build.slave_info = {} |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
147 build.started = 0 |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
148 db = self.env.get_db_cnx() |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
149 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
|
150 step.delete(db=db) |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
151 build.update(db=db) |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
152 db.commit() |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
153 |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
154 req.send_response(204) |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
155 req.write('') |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
156 raise RequestDone |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
419
diff
changeset
|
157 |
392 | 158 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
|
159 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
|
160 build.id) |
392 | 161 build.started = int(time.time()) |
162 build.update() | |
277 | 163 |
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
|
164 xml = xmlio.parse(config.recipe) |
392 | 165 xml.attr['path'] = config.path |
166 xml.attr['revision'] = build.rev | |
167 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
|
168 |
392 | 169 req.send_response(200) |
170 req.send_header('Content-Type', 'application/x-bitten+xml') | |
171 req.send_header('Content-Length', str(len(body))) | |
172 req.send_header('Content-Disposition', | |
173 'attachment; filename=recipe_%s_r%s.xml' % | |
174 (config.name, build.rev)) | |
175 req.write(body) | |
176 raise RequestDone | |
227
014bc6c29dff
* Factor build queue logic into a class separate from the build master.
cmlenz
parents:
213
diff
changeset
|
177 |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
178 def _process_build_step(self, req, config, build): |
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
179 try: |
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
180 elem = xmlio.parse(req.read()) |
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
181 except xmlio.ParseError, e: |
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
182 raise HTTPBadRequest('XML parser error') |
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
183 stepname = elem.attr['step'] |
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
184 |
392 | 185 step = BuildStep.fetch(self.env, build=build.id, name=stepname) |
186 if step: | |
187 raise HTTPConflict('Build step already exists') | |
253 | 188 |
392 | 189 recipe = Recipe(xmlio.parse(config.recipe)) |
190 index = None | |
191 current_step = None | |
192 for num, recipe_step in enumerate(recipe): | |
193 if recipe_step.id == stepname: | |
194 index = num | |
195 current_step = recipe_step | |
196 if index is None: | |
197 raise HTTPForbidden('No such build step') | |
198 last_step = index == num | |
200
692924ffed80
Changes to the BDB XML report store to support transactions. Closes #47.
cmlenz
parents:
197
diff
changeset
|
199 |
392 | 200 self.log.debug('Slave %s completed step %d (%s) with status %s', |
201 build.slave, index, stepname, elem.attr['status']) | |
202 | |
203 db = self.env.get_db_cnx() | |
204 | |
205 step = BuildStep(self.env, build=build.id, name=stepname) | |
206 try: | |
207 step.started = int(_parse_iso_datetime(elem.attr['time'])) | |
208 step.stopped = step.started + float(elem.attr['duration']) | |
209 except ValueError, e: | |
210 raise HTTPBadRequest(e.args[0]) | |
211 if elem.attr['status'] == 'failure': | |
212 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
|
213 step.status = BuildStep.FAILURE |
5bf22bb87915
Transmit build log and generated data back to the build master in XML format. Closes #23.
cmlenz
parents:
96
diff
changeset
|
214 else: |
5bf22bb87915
Transmit build log and generated data back to the build master in XML format. Closes #23.
cmlenz
parents:
96
diff
changeset
|
215 step.status = BuildStep.SUCCESS |
277 | 216 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
|
217 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
|
218 |
392 | 219 # 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
|
220 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
|
221 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
|
222 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
|
223 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
|
224 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
|
225 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
|
226 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
|
227 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
|
228 |
392 | 229 # 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
|
230 for report_elem in elem.children('report'): |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
231 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
|
232 category=report_elem.attr.get('category'), |
25f84dd9f159
* Refactoring of build recipes, the file format has changed slightly:
cmlenz
parents:
204
diff
changeset
|
233 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
|
234 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
|
235 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
|
236 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
|
237 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
|
238 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
|
239 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
|
240 report.insert(db=db) |
116 | 241 |
392 | 242 # If this was the last step in the recipe we mark the build as |
243 # completed | |
244 if last_step or step.status == BuildStep.FAILURE and \ | |
245 current_step.onerror == 'fail': | |
246 self.log.info('Slave %s completed build %d ("%s" as of [%s])', | |
247 build.slave, build.id, build.config, build.rev) | |
248 build.stopped = step.stopped | |
249 | |
250 # Determine overall outcome of the build by checking the outcome | |
251 # of the individual steps against the "onerror" specification of | |
252 # each step in the recipe | |
253 for num, recipe_step in enumerate(recipe): | |
254 step = BuildStep.fetch(self.env, build.id, recipe_step.id) | |
255 if step.status == BuildStep.FAILURE: | |
256 if recipe_step.onerror != 'ignore': | |
257 build.status = Build.FAILURE | |
258 break | |
259 else: | |
260 build.status = Build.SUCCESS | |
261 | |
262 build.update(db=db) | |
263 | |
200
692924ffed80
Changes to the BDB XML report store to support transactions. Closes #47.
cmlenz
parents:
197
diff
changeset
|
264 db.commit() |
692924ffed80
Changes to the BDB XML report store to support transactions. Closes #47.
cmlenz
parents:
197
diff
changeset
|
265 |
392 | 266 if last_step: |
267 for listener in BuildSystem(self.env).listeners: | |
268 listener.build_completed(build) | |
253 | 269 |
392 | 270 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
|
271 req.send_response(201) |
392 | 272 req.send_header('Content-Type', 'text/plain') |
273 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
|
274 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
|
275 stepname)) |
392 | 276 req.write(body) |
277 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
|
278 |
13
21aa17f97522
Initial code for build master and slave... these don't do a lot yet.
cmlenz
parents:
diff
changeset
|
279 |
82
01200c88ddb0
Include timing information in the build messages transmitted from slave to master for better accuracy.
cmlenz
parents:
80
diff
changeset
|
280 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
|
281 """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
|
282 |
01200c88ddb0
Include timing information in the build messages transmitted from slave to master for better accuracy.
cmlenz
parents:
80
diff
changeset
|
283 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
|
284 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
|
285 try: |
01200c88ddb0
Include timing information in the build messages transmitted from slave to master for better accuracy.
cmlenz
parents:
80
diff
changeset
|
286 string = string.split('.', 1)[0] # strip out microseconds |
312 | 287 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
|
288 except ValueError, e: |
392 | 289 raise ValueError('Invalid ISO date/time %r' % string) |