Merge lp:~vila/uci-engine/britney-process-reports into lp:uci-engine
- britney-process-reports
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Vincent Ladeuil |
Approved revision: | 918 |
Merged at revision: | 873 |
Proposed branch: | lp:~vila/uci-engine/britney-process-reports |
Merge into: | lp:uci-engine |
Prerequisite: | lp:~vila/uci-engine/fix-britney-results |
Diff against target: |
701 lines (+501/-108) 8 files modified
britney_proxy/bin/uci-britney-report (+63/-0) britney_proxy/bin/uci-britney-request (+5/-3) britney_proxy/britney/config.py (+83/-0) britney_proxy/britney/post_request.py (+3/-67) britney_proxy/britney/process_reports.py (+179/-0) britney_proxy/britney/tests/test_config.py (+55/-0) britney_proxy/britney/tests/test_post_request.py (+1/-38) britney_proxy/britney/tests/test_process_reports.py (+112/-0) |
To merge this branch: | bzr merge lp:~vila/uci-engine/britney-process-reports |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Francis Ginther | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Martin Pitt | Pending | ||
Review via email: mp+240180@code.launchpad.net |
Commit message
Process britney test result reports.
Description of the change
This is the last bit of the britney-proxy which can be used on jenkins or to validate the migration (among other tools).
It includes some slight refactoring to share more code but really build on
top of the previous parts, the "real" work is done in
britney_
Followups MPs will factorize the relevant pieces so a new service can be implemented with less paperwork ;)
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:909
http://
Executed test runs:
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:912
http://
Executed test runs:
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:914
http://
Executed test runs:
Click here to trigger a rebuild:
http://
Vincent Ladeuil (vila) wrote : | # |
What this MP provides may be clearer when looking at its output (sample from a live run):
2014-10-31 00:53:23,951 INFO trusty amd64 zope.interface
2014-10-31 00:53:24,741 INFO utopic amd64 abi-compliance-
2014-10-31 00:53:48,177 INFO trusty amd64 zope.testing
2014-10-31 00:54:18,008 INFO trusty amd64 zope.session
2014-10-31 00:54:31,651 INFO trusty amd64 zope.testrunner
2014-10-31 00:54:32,796 INFO trusty amd64 zope.formlib
2014-10-31 00:55:37,291 INFO utopic amd64 adduser
2014-10-31 00:55:59,448 INFO utopic amd64 alglib
2014-10-31 00:56:00,395 INFO utopic amd64 apache2
2014-10-31 00:56:25,780 INFO utopic amd64 apparmor-
2014-10-31 00:56:42,962 INFO utopic amd64 appstream
2014-10-31 00:56:55,261 INFO utopic amd64 apq-postgresql
2014-10-31 00:57:32,190 INFO utopic amd64 adequate
Measures included ;)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:915
http://
Executed test runs:
Click here to trigger a rebuild:
http://
Martin Pitt (pitti) wrote : | # |
I have some nitpicks and a question below. In general I'm still unsure why we need a "britney report/result queue". We will *not* listen to a queue in britney, that just doesn't conceptually work. We want to poll a results tree (like trusty/
Vincent Ladeuil (vila) wrote : | # |
> I have some nitpicks and a question below. In general I'm still unsure why we
> need a "britney report/result queue". We will *not* listen to a queue in
> britney, that just doesn't conceptually work. We want to poll a results tree
> (like trusty/
> (i. e. a network file system); preferably swift as that's where we write the
> results to, and we should not unnecessarily put extra stuff in between which
> can again fail :-)
Yup, that's what I want to reach asap without blocking the validation which can progress with this MP.
Vincent Ladeuil (vila) wrote : | # |
Grr, lp didn't save the comments in the previous reply
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:916
http://
Executed test runs:
Click here to trigger a rebuild:
http://
Francis Ginther (fginther) wrote : | # |
Just a few questions and a typo. Otherwise looks good.
Vincent Ladeuil (vila) wrote : | # |
All fixed, replies inline.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:918
http://
Executed test runs:
Click here to trigger a rebuild:
http://
Ubuntu CI Bot (uci-bot) wrote : | # |
The attempt to merge lp:~vila/uci-engine/britney-process-reports into lp:uci-engine failed. Below is the output from the failed tests.
Running cm...
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
uploading webui-content.tgz to swift
Updating source dependencies...
2014-11-01 10:48:20 INFO juju.cmd supercommand.go:37 running jujud [1.20.11.
2014-11-01 10:48:20 DEBUG juju.agent agent.go:377 read agent config, format "1.18"
2014-11-01 10:48:20 INFO juju.jujud unit.go:78 unit agent unit-ci-
2014-11-01 10:48:20 INFO juju.worker runner.go:260 start "api"
2014-11-01 10:48:20 INFO juju.state.api apiclient.go:242 dialing "wss://
2014-11-01 10:48:20 INFO juju.state.api apiclient.go:176 connection established to "wss://
2014-11-01 10:48:20 INFO juju.state.api apiclient.go:242 dialing "wss://
2014-11-01 10:48:20 INFO juju.state.api apiclient.go:176 connection established to "wss://
2014-11-01 10:48:21 INFO juju.state.api apiclient.go:242 dialing "wss://
2014-11-01 10:48:21 INFO juju.state.api apiclient.go:176 connection established to "wss://
2014-11-01 10:48:22 INFO juju.worker runner.go:260 start "upgrader"
2014-11-01 10:48:22 INFO juju.worker runner.go:260 start "logger"
2014-11-01 10:48:22 DEBUG juju.worker.logger logger.go:35 initial log config: "<root>=DEBUG"
2014-11-01 10:48:22 INFO juju.worker runner.go:260 start "uniter"
2014-11-01 10:48:22 DEBUG juju.worker.logger logger.go:60 logger setup
2014-11-01 10:48:22 INFO juju.worker runner.go:260 start "apiaddressupdater"
2014-11-01 10:48:22 INFO juju.worker runner.go:260 start "rsyslog"
2014-11-01 10:48:22 DEBUG juju.worker.rsyslog worker.go:75 starting rsyslog worker mode 1 for "unit-ci-
2014-11-01 10:48:22 DEBUG juju.worker.logger logger.go:45 reconfiguring logging from "<root>=DEBUG" to "<root>
2014-11-01 10:48:39 INFO juju-log Making dir /srv/ci-
2014-11-01 10:48:39 INFO juju-log Adding dependencies.
2014-11-01 10:48:39 INFO juju-log installing apt packages...
2014-11-01 10:48:40 INFO config-changed gpg: keyring `/tmp/tmpGfAGA_
2014-11-01 10:48:40 INFO config-changed gpg: keyring `/tmp/tmpGfAGA_
2014-11-01 10:48:40 INFO config-changed gpg: requesting key 6A8DFC40 from hkp server keyserver.
2014-11-01 10:48:40 INFO config-changed gpg: /tmp/tmpGfAGA_
2014-11-01 10:48:40 INFO config-changed gpg: key 6A8DFC40: public key "Launchpad PPA for Canonical CI Engineering" imported
2014-11-01 10:48:40 INFO config-changed gpg: Total number processed: 1
2014-11-01 10:48:40 INFO config-changed gpg: imported: 1 (RSA: 1)
2014-11-01 10:48:41 INFO config-changed OK
2014-11-01 10:48:43 INFO config-changed Hit http://
2014-11-01 10:48:43 INFO config-chan...
Vincent Ladeuil (vila) wrote : | # |
2014-11-01 11:05:02 INFO pgsql-relation-
2014-11-01 11:05:02 INFO pgsql-relation-
2014-11-01 11:05:02 INFO pgsql-relation-
2014-11-01 11:05:02 INFO juju-log pgsql:28: migration command failed, retrying...
2014-11-01 11:05:08 INFO pgsql-relation-
2014-11-01 11:05:08 INFO pgsql-relation-
2014-11-01 11:05:08 INFO pgsql-relation-
2014-11-01 11:05:08 INFO pgsql-relation-
2014-11-01 11:05:08 INFO juju-log pgsql:28: migration command failed, retrying...
2014-11-01 11:05:13 INFO pgsql-relation-
2014-11-01 11:05:13 INFO pgsql-relation-
2014-11-01 11:05:13 INFO pgsql-relation-
2014-11-01 11:05:13 INFO pgsql-relation-
2014-11-01 11:05:13 INFO juju-log pgsql:28: migration command failed, retrying...
2014-11-01 11:05:19 INFO pgsql-relation-
2014-11-01 11:05:19 INFO pgsql-relation-
2014-11-01 11:05:19 INFO pgsql-relation-
2014-11-01 11:05:19 INFO pgsql-relation-
2014-11-01 11:05:19 INFO juju-log pgsql:28: migration command failed, retrying...
2014-11-01 11:05:25 INFO pgsql-relation-
2014-11-01 11:05:25 INFO pgsql-relation-
2014-11-01 11:05:25 INFO pgsql-relation-
2014-11-01 11:05:25 INFO pgsql-relation-
2014-11-01 11:05:25 INFO juju-log pgsql:28: migration command failed, retrying...
2014-11-01 11:05:34 INFO juju-log pgsql:28: migration command failed
2014-11-01 11:05:34 INFO pgsql-relation-
2014-11-01 11:05:34 INFO pgsql-relation-
2014-11-01 11:05:34 INFO pgsql-relation-
2014-11-01 11:05:34 INFO pgsql-relation-
2014-11-01 11:05:34 INFO pgsql-relation-
2014-11-01 11:05:34 INFO pgsql-relation-
2014-11-01 11:05:34 INFO pgsql-relation-
2014-11-01 11:05:34 INFO pgsql-relation-
2014-11-01 11:05:34 INFO pgsql-relation-
Preview Diff
1 | === added file 'britney_proxy/bin/uci-britney-report' |
2 | --- britney_proxy/bin/uci-britney-report 1970-01-01 00:00:00 +0000 |
3 | +++ britney_proxy/bin/uci-britney-report 2014-10-31 16:39:46 +0000 |
4 | @@ -0,0 +1,63 @@ |
5 | +#!/usr/bin/env python |
6 | +# Ubuntu CI Engine |
7 | +# Copyright 2014 Canonical Ltd. |
8 | + |
9 | +# This program is free software: you can redistribute it and/or modify it |
10 | +# under the terms of the GNU Affero General Public License version 3, as |
11 | +# published by the Free Software Foundation. |
12 | + |
13 | +# This program is distributed in the hope that it will be useful, but |
14 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
15 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
16 | +# PURPOSE. See the GNU Affero General Public License for more details. |
17 | + |
18 | +# You should have received a copy of the GNU Affero General Public License |
19 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 | +"""The script to collect britney results from outside the uci-engine.""" |
21 | + |
22 | +import logging |
23 | +import os |
24 | +import sys |
25 | + |
26 | +# FIXME: Add -n to handle only 'n' requests and quit (default to 1, 0 to loop |
27 | +# forver) -- vila 2014-10-30 |
28 | + |
29 | +# FIXME: 'sys.path.{insert,append}' is evil and should be fixed when |
30 | +# packaging uci-engine or part of it. -- vila 2014-10-15 |
31 | + |
32 | +HERE = os.path.abspath(os.path.dirname(__file__)) |
33 | +# We want to be able to import from the branch root |
34 | +BRANCH_ROOT = os.path.abspath(os.path.join(HERE, '..', '..')) |
35 | +sys.path.insert(0, os.path.join(BRANCH_ROOT, 'britney_proxy')) |
36 | +sys.path.insert(0, os.path.join(BRANCH_ROOT, 'ci-utils')) |
37 | + |
38 | + |
39 | +from uciconfig import errors |
40 | + |
41 | + |
42 | +from britney import ( |
43 | + config, |
44 | + process_reports, |
45 | +) |
46 | +from ci_utils import ( |
47 | + amqp_utils, |
48 | + dump_stack, |
49 | +) |
50 | + |
51 | + |
52 | +if __name__ == '__main__': |
53 | + dump_stack.install_stack_dump_signal() |
54 | + logging.basicConfig(level=logging.INFO, |
55 | + format="%(asctime)s %(levelname)s %(message)s") |
56 | + # Force amqp_utils to use our config instead of the non existant |
57 | + # amqp_config.py one (only available for rabbit workers in uci-engine). |
58 | + amqp_utils.get_config = config.amqp_config(config.BritneyStack()) |
59 | + |
60 | + try: |
61 | + ret = process_reports.cli_run(sys.argv[1:], sys.stdout, sys.stderr) |
62 | + except errors.ConfigError as e: |
63 | + # FIXME: If one option is missing, the other mandatory ones are |
64 | + # propably missing too, report them all. -- vila 2014-10-16 |
65 | + stderr.write('{}\n'.format(e)) |
66 | + ret = 1 |
67 | + sys.exit(ret) |
68 | |
69 | === modified file 'britney_proxy/bin/uci-britney-request' |
70 | --- britney_proxy/bin/uci-britney-request 2014-10-30 19:31:08 +0000 |
71 | +++ britney_proxy/bin/uci-britney-request 2014-10-31 16:39:46 +0000 |
72 | @@ -32,15 +32,17 @@ |
73 | from uciconfig import errors |
74 | |
75 | |
76 | -from britney import post_request |
77 | +from britney import ( |
78 | + config, |
79 | + post_request, |
80 | +) |
81 | from ci_utils import amqp_utils |
82 | |
83 | |
84 | if __name__ == '__main__': |
85 | # Force amqp_utils to use our config instead of the non existant |
86 | # amqp_config.py one (only available for rabbit workers in uci-engine). |
87 | - amqp_utils.get_config = post_request.amqp_config( |
88 | - post_request.BritneyStack()) |
89 | + amqp_utils.get_config = config.amqp_config(config.BritneyStack()) |
90 | try: |
91 | ret = post_request.cli_run(sys.argv[1:], sys.stdout, sys.stderr) |
92 | except errors.ConfigError as e: |
93 | |
94 | === added file 'britney_proxy/britney/config.py' |
95 | --- britney_proxy/britney/config.py 1970-01-01 00:00:00 +0000 |
96 | +++ britney_proxy/britney/config.py 2014-10-31 16:39:46 +0000 |
97 | @@ -0,0 +1,83 @@ |
98 | +# Ubuntu CI Engine |
99 | +# Copyright 2014 Canonical Ltd. |
100 | + |
101 | +# This program is free software: you can redistribute it and/or modify it |
102 | +# under the terms of the GNU Affero General Public License version 3, as |
103 | +# published by the Free Software Foundation. |
104 | + |
105 | +# This program is distributed in the hope that it will be useful, but |
106 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
107 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
108 | +# PURPOSE. See the GNU Affero General Public License for more details. |
109 | + |
110 | +# You should have received a copy of the GNU Affero General Public License |
111 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
112 | +"""Configuration options related to the britney proxy.""" |
113 | + |
114 | +import os |
115 | + |
116 | + |
117 | +from uciconfig import ( |
118 | + options, |
119 | + stacks, |
120 | + stores, |
121 | +) |
122 | + |
123 | + |
124 | +BritneyStore = stores.FileStore |
125 | + |
126 | + |
127 | +def user_config_dir(): |
128 | + conf_dir = os.environ.get('XDG_CONFIG_HOME', |
129 | + os.path.expanduser('~/.config')) |
130 | + return conf_dir |
131 | + |
132 | + |
133 | +class BritneyStack(stacks.Stack): |
134 | + """Per-user options.""" |
135 | + |
136 | + def __init__(self): |
137 | + """Make a new stack for britney interactions with the uci engine. |
138 | + |
139 | + The options are searched in ~/.config/uci-britney.conf. |
140 | + """ |
141 | + upath = os.path.join(user_config_dir(), 'uci-britney.conf') |
142 | + user_store = self.get_shared_store(BritneyStore(upath)) |
143 | + super(BritneyStack, self).__init__( |
144 | + # We only care about the no name section |
145 | + [stacks.NameMatcher(user_store, None).get_sections], |
146 | + user_store, mutable_section_id=None) |
147 | + |
148 | + |
149 | +def register(option): |
150 | + options.option_registry.register(option) |
151 | + |
152 | + |
153 | +register(options.Option('uci.rabbit.host', |
154 | + default=options.MANDATORY, |
155 | + help_string='''\ |
156 | +The host:port address for the uci-britney rabbit server. |
157 | +''')) |
158 | +register(options.Option('uci.rabbit.user', |
159 | + default=options.MANDATORY, |
160 | + help_string='''\ |
161 | +The user name for the uci-britney rabbit server. |
162 | +''')) |
163 | +register(options.Option('uci.rabbit.password', |
164 | + default=options.MANDATORY, |
165 | + help_string='''\ |
166 | +The user password for the uci-britney rabbit server. |
167 | +''')) |
168 | + |
169 | + |
170 | +def amqp_config(conf): |
171 | + """Create the rabbit config from the britney one.""" |
172 | + class AmqpConfig(object): |
173 | + |
174 | + def __init__(self): |
175 | + self.AMQP_HOST = conf.get('uci.rabbit.host') |
176 | + self.AMQP_VHOST = '/' |
177 | + self.AMQP_USER = conf.get('uci.rabbit.user') |
178 | + self.AMQP_PASSWORD = conf.get('uci.rabbit.password') |
179 | + |
180 | + return AmqpConfig |
181 | |
182 | === modified file 'britney_proxy/britney/post_request.py' |
183 | --- britney_proxy/britney/post_request.py 2014-10-15 14:55:04 +0000 |
184 | +++ britney_proxy/britney/post_request.py 2014-10-31 16:39:46 +0000 |
185 | @@ -15,17 +15,9 @@ |
186 | |
187 | |
188 | import argparse |
189 | -import os |
190 | import sys |
191 | |
192 | |
193 | -from uciconfig import ( |
194 | - options, |
195 | - stacks, |
196 | - stores, |
197 | -) |
198 | - |
199 | - |
200 | from britney import queues |
201 | |
202 | |
203 | @@ -87,74 +79,18 @@ |
204 | sys.stderr = err_orig |
205 | |
206 | |
207 | -BritneyStore = stores.FileStore |
208 | - |
209 | - |
210 | -def user_config_dir(): |
211 | - conf_dir = os.environ.get('XDG_CONFIG_HOME', |
212 | - os.path.expanduser('~/.config')) |
213 | - return conf_dir |
214 | - |
215 | - |
216 | -class BritneyStack(stacks.Stack): |
217 | - """Per-user options.""" |
218 | - |
219 | - def __init__(self): |
220 | - """Make a new stack for britney interactions with the uci engine. |
221 | - |
222 | - The options are searched in ~/.config/uci-britney.conf. |
223 | - """ |
224 | - upath = os.path.join(user_config_dir(), 'uci-britney.conf') |
225 | - user_store = self.get_shared_store(BritneyStore(upath)) |
226 | - super(BritneyStack, self).__init__( |
227 | - # We only care about the no name section |
228 | - [stacks.NameMatcher(user_store, None).get_sections], |
229 | - user_store, mutable_section_id=None) |
230 | - |
231 | - |
232 | -def register(option): |
233 | - options.option_registry.register(option) |
234 | - |
235 | - |
236 | -register(options.Option('uci.rabbit.host', |
237 | - default=options.MANDATORY, |
238 | - help_string='''\ |
239 | -The host:port address for the uci-britney rabbit server. |
240 | -''')) |
241 | -register(options.Option('uci.rabbit.user', |
242 | - default=options.MANDATORY, |
243 | - help_string='''\ |
244 | -The user name for the uci-britney rabbit server. |
245 | -''')) |
246 | -register(options.Option('uci.rabbit.password', |
247 | - default=options.MANDATORY, |
248 | - help_string='''\ |
249 | -The user password for the uci-britney rabbit server. |
250 | -''')) |
251 | - |
252 | - |
253 | def post(queue, series, architecture, package, output=None, report=None): |
254 | msg = dict(series=series, architecture=architecture, package=package) |
255 | if output is not None: |
256 | msg['output_queue'] = output |
257 | if report is not None: |
258 | msg['report_queue'] = report |
259 | + # FIXME: Error checking to guarantee that we at least give a proper error |
260 | + # message. Known failures: bad host (can't connect), bad credentials (can't |
261 | + # connect). Possible failures: rabbit down. -- vila 2014-10-24 |
262 | queue.publish(msg) |
263 | |
264 | |
265 | -def amqp_config(conf): |
266 | - """Create the rabbit config from the britney one.""" |
267 | - class AmqpConfig(object): |
268 | - |
269 | - def __init__(self): |
270 | - self.AMQP_HOST = conf.get('uci.rabbit.host') |
271 | - self.AMQP_VHOST = '/' |
272 | - self.AMQP_USER = conf.get('uci.rabbit.user') |
273 | - self.AMQP_PASSWORD = conf.get('uci.rabbit.password') |
274 | - |
275 | - return AmqpConfig |
276 | - |
277 | - |
278 | def cli_run(args, stdout, stderr): |
279 | """Post a britney test request from the command line.""" |
280 | parser = PostRequestArgParser() |
281 | |
282 | === added file 'britney_proxy/britney/process_reports.py' |
283 | --- britney_proxy/britney/process_reports.py 1970-01-01 00:00:00 +0000 |
284 | +++ britney_proxy/britney/process_reports.py 2014-10-31 16:39:46 +0000 |
285 | @@ -0,0 +1,179 @@ |
286 | +#!/usr/bin/env python |
287 | +# Ubuntu CI Engine |
288 | +# Copyright 2014 Canonical Ltd. |
289 | + |
290 | +# This program is free software: you can redistribute it and/or modify it |
291 | +# under the terms of the GNU Affero General Public License version 3, as |
292 | +# published by the Free Software Foundation. |
293 | + |
294 | +# This program is distributed in the hope that it will be useful, but |
295 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
296 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
297 | +# PURPOSE. See the GNU Affero General Public License for more details. |
298 | + |
299 | +# You should have received a copy of the GNU Affero General Public License |
300 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
301 | +from __future__ import unicode_literals |
302 | + |
303 | + |
304 | +import argparse |
305 | +import errno |
306 | +import os |
307 | +import json |
308 | +import logging |
309 | +import sys |
310 | + |
311 | + |
312 | +from ci_utils import amqp_utils |
313 | + |
314 | + |
315 | +class BritneyReportHandler(object): |
316 | + """Process a britney test report. |
317 | + |
318 | + This is fed by messages received on a queue or directly for tests by |
319 | + calling the ``handle()`` method. |
320 | + """ |
321 | + |
322 | + def __init__(self, output_dir): |
323 | + self.output_dir = output_dir |
324 | + try: |
325 | + # Try to create the needed dirs |
326 | + os.makedirs(self.output_dir) |
327 | + except OSError as e: |
328 | + # They are already there, no worries |
329 | + if e.errno != errno.EEXIST: |
330 | + raise |
331 | + |
332 | + def handle(self, request): |
333 | + """Handle a report request. |
334 | + |
335 | + :param request: The request describing a britney result to process. |
336 | + |
337 | + This creates a file in the 'output_dir' directory under a name and with |
338 | + the content expected by britney. |
339 | + |
340 | + The name is something like: |
341 | + {series}_{arch}_{package}-{DATE}_{HOUR}[_{whatever}].result |
342 | + |
343 | + The content is (all fields on a single line separated by spaces): |
344 | + {series} {arch} {package} {version} {PASS|FAILED} [{dep} {dep_version]* |
345 | + |
346 | + If an error occured while setting up the testbed (by adt-run) we get |
347 | + only: |
348 | + {series} {arch} {package} |
349 | + """ |
350 | + |
351 | + # From 'result_at' we get info for the path |
352 | + store_prefix = request['result_at'] |
353 | + # The prefix has the timestamp |
354 | + # Sample: trusty/amd64/libp/libpng/20141001_000000_localhost |
355 | + stamp = store_prefix.split('/')[4] |
356 | + day, hour = stamp.split('_')[:2] |
357 | + |
358 | + # From 'result' we get the series, arch and package parts |
359 | + result = request['britney_result'] |
360 | + # The first 3 fields are guaranteed to exist. |
361 | + series, arch, package = result.split()[:3] |
362 | + logging.info('{} {} {}'.format(series, arch, package)) |
363 | + |
364 | + path = os.path.join(self.output_dir, '{}_{}_{}_{}-{}.result'.format( |
365 | + series, arch, package, day, hour)) |
366 | + with open(path, 'wb') as f: |
367 | + f.write(result) |
368 | + |
369 | + |
370 | +class BritneyReportsQueue(object): |
371 | + """The queue receiving the reports for the britney results.""" |
372 | + |
373 | + def __init__(self, queue_name, request_handler, max_reports=0): |
374 | + """Britney reports queue. |
375 | + |
376 | + :param queue_name: The queue name where the reports are received. |
377 | + |
378 | + :param request_handler: The handler that processes the reports. |
379 | + |
380 | + :param max_reports: Max number of reports to handle before |
381 | + quitting. The default value (0) is used to never quit. |
382 | + """ |
383 | + self.queue_name = queue_name |
384 | + self.request_handler = request_handler |
385 | + self.max_reports = max_reports |
386 | + self.nb_reports = 0 |
387 | + |
388 | + def consume(self): |
389 | + conf = amqp_utils.get_config() |
390 | + amqp_utils.process_queue(conf, self.queue_name, self.handle_request) |
391 | + |
392 | + def handle_request(self, msg): |
393 | + logging.debug('Will handle {}'.format(msg.body)) |
394 | + body = json.loads(msg.body) |
395 | + ret = self.request_handler.handle(body) |
396 | + msg.channel.basic_ack(msg.delivery_tag) |
397 | + logging.debug('Handling returned: {} for {}'.format(ret, msg.body)) |
398 | + self.nb_reports += 1 |
399 | + if self.max_reports and self.nb_reports >= self.max_reports: |
400 | + # Leave the loop |
401 | + msg.channel.basic_cancel(msg.consumer_tag) |
402 | + return ret |
403 | + |
404 | + |
405 | +class ProcessReportArgParser(argparse.ArgumentParser): |
406 | + |
407 | + def __init__(self, prog=None, description=None): |
408 | + if prog is None: |
409 | + prog = 'uci-britney-report' |
410 | + if description is None: |
411 | + description = 'Process a test report from britney.' |
412 | + super(ProcessReportArgParser, self).__init__(prog=prog, |
413 | + description=description) |
414 | + self.add_argument( |
415 | + 'report', metavar='REPORT_QUEUE', |
416 | + help='The REPORT_QUEUE publishing the test results.') |
417 | + self.add_argument( |
418 | + 'output', metavar='OUTPUT', |
419 | + help='The OUTPUT directory where the results are stored.') |
420 | + # Optional arguments |
421 | + self.add_argument( |
422 | + '--nb-reports', '-n', metavar='REPORTS', default=1, type=int, |
423 | + help='The number of reports to process from the queue' |
424 | + ' named REPORT_QUEUE.') |
425 | + |
426 | + def parse_args(self, args=None, out=None, err=None): |
427 | + """Parse arguments, overridding stdout/stderr if provided. |
428 | + |
429 | + Overridding stdout/stderr is provided for tests to address arpgarse |
430 | + hardcoding sys.{stdout,stderr} usage. |
431 | + |
432 | + :params args: The arguments to the script. |
433 | + |
434 | + :param out: Default to sys.stdout. |
435 | + |
436 | + :param err: Default to sys.stderr. |
437 | + |
438 | + :return: The populated namespace. |
439 | + |
440 | + """ |
441 | + out_orig = sys.stdout |
442 | + err_orig = sys.stderr |
443 | + try: |
444 | + if out is not None: |
445 | + sys.stdout = out |
446 | + if err is not None: |
447 | + sys.stderr = err |
448 | + return super(ProcessReportArgParser, self).parse_args(args) |
449 | + finally: |
450 | + sys.stdout = out_orig |
451 | + sys.stderr = err_orig |
452 | + |
453 | + |
454 | +def cli_run(args, stdout, stderr): |
455 | + """Process britney test reports from the command line.""" |
456 | + parser = ProcessReportArgParser() |
457 | + opts = parser.parse_args(args, out=stdout, err=stderr) |
458 | + request_handler = BritneyReportHandler(opts.output) |
459 | + queue = BritneyReportsQueue(opts.report, request_handler, |
460 | + max_reports=opts.nb_reports) |
461 | + if opts.nb_reports == 0: |
462 | + sys.stderr.write('Waiting for reports. ^C to exit.') |
463 | + queue.consume() |
464 | + return 0 |
465 | |
466 | === added file 'britney_proxy/britney/tests/test_config.py' |
467 | --- britney_proxy/britney/tests/test_config.py 1970-01-01 00:00:00 +0000 |
468 | +++ britney_proxy/britney/tests/test_config.py 2014-10-31 16:39:46 +0000 |
469 | @@ -0,0 +1,55 @@ |
470 | +# Copyright 2014 Canonical Ltd. |
471 | + |
472 | +# This program is free software: you can redistribute it and/or modify it |
473 | +# under the terms of the GNU Affero General Public License version 3, as |
474 | +# published by the Free Software Foundation. |
475 | + |
476 | +# This program is distributed in the hope that it will be useful, but |
477 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
478 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
479 | +# PURPOSE. See the GNU Affero General Public License for more details. |
480 | + |
481 | +# You should have received a copy of the GNU Affero General Public License |
482 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
483 | + |
484 | +import os |
485 | +import unittest |
486 | + |
487 | + |
488 | +from ucitests import fixtures |
489 | + |
490 | + |
491 | +from britney import config |
492 | + |
493 | + |
494 | +class TestConfigDir(unittest.TestCase): |
495 | + |
496 | + def setUp(self): |
497 | + super(TestConfigDir, self).setUp() |
498 | + fixtures.set_uniq_cwd(self) |
499 | + fixtures.isolate_from_env(self, dict(HOME=self.uniq_dir, |
500 | + XDG_CONFIG_HOME=None)) |
501 | + |
502 | + def test_default(self): |
503 | + conf_dir = os.path.join(self.uniq_dir, '.config') |
504 | + self.assertEqual(conf_dir, config.user_config_dir()) |
505 | + |
506 | + def test_xdg_config_home_override(self): |
507 | + xdg_dir = os.path.join(self.uniq_dir, 'xdg') |
508 | + os.environ['XDG_CONFIG_HOME'] = xdg_dir |
509 | + self.assertEqual(xdg_dir, config.user_config_dir()) |
510 | + |
511 | + |
512 | +class TestConfig(unittest.TestCase): |
513 | + |
514 | + def setUp(self): |
515 | + super(TestConfig, self).setUp() |
516 | + fixtures.set_uniq_cwd(self) |
517 | + fixtures.isolate_from_env(self, dict(HOME=self.uniq_dir, |
518 | + XDG_CONFIG_HOME=None)) |
519 | + self.config = config.BritneyStack() |
520 | + |
521 | + def test_empty_config_errors(self): |
522 | + with self.assertRaises(Exception) as cm: |
523 | + self.config.get('uci.rabbit.host') |
524 | + self.assertEqual('uci.rabbit.host must be set.', unicode(cm.exception)) |
525 | |
526 | === modified file 'britney_proxy/britney/tests/test_post_request.py' |
527 | --- britney_proxy/britney/tests/test_post_request.py 2014-10-15 14:29:40 +0000 |
528 | +++ britney_proxy/britney/tests/test_post_request.py 2014-10-31 16:39:46 +0000 |
529 | @@ -13,14 +13,10 @@ |
530 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
531 | |
532 | from cStringIO import StringIO |
533 | -import os |
534 | import unittest |
535 | |
536 | |
537 | -from ucitests import ( |
538 | - assertions, |
539 | - fixtures as uci_fixtures, |
540 | -) |
541 | +from ucitests import assertions |
542 | |
543 | |
544 | from britney import post_request |
545 | @@ -60,39 +56,6 @@ |
546 | self.assertEqual('', self.err.getvalue()) |
547 | |
548 | |
549 | -class TestConfigDir(unittest.TestCase): |
550 | - |
551 | - def setUp(self): |
552 | - super(TestConfigDir, self).setUp() |
553 | - uci_fixtures.set_uniq_cwd(self) |
554 | - uci_fixtures.isolate_from_env(self, dict(HOME=self.uniq_dir, |
555 | - XDG_CONFIG_HOME=None)) |
556 | - |
557 | - def test_default(self): |
558 | - conf_dir = os.path.join(self.uniq_dir, '.config') |
559 | - self.assertEqual(conf_dir, post_request.user_config_dir()) |
560 | - |
561 | - def test_xdg_config_home_override(self): |
562 | - xdg_dir = os.path.join(self.uniq_dir, 'xdg') |
563 | - os.environ['XDG_CONFIG_HOME'] = xdg_dir |
564 | - self.assertEqual(xdg_dir, post_request.user_config_dir()) |
565 | - |
566 | - |
567 | -class TestConfig(unittest.TestCase): |
568 | - |
569 | - def setUp(self): |
570 | - super(TestConfig, self).setUp() |
571 | - uci_fixtures.set_uniq_cwd(self) |
572 | - uci_fixtures.isolate_from_env(self, dict(HOME=self.uniq_dir, |
573 | - XDG_CONFIG_HOME=None)) |
574 | - self.config = post_request.BritneyStack() |
575 | - |
576 | - def test_empty_config_errors(self): |
577 | - with self.assertRaises(Exception) as cm: |
578 | - self.config.get('uci.rabbit.host') |
579 | - self.assertEqual('uci.rabbit.host must be set.', unicode(cm.exception)) |
580 | - |
581 | - |
582 | class TestPost(unittest.TestCase): |
583 | |
584 | def test_post_simple(self): |
585 | |
586 | === added file 'britney_proxy/britney/tests/test_process_reports.py' |
587 | --- britney_proxy/britney/tests/test_process_reports.py 1970-01-01 00:00:00 +0000 |
588 | +++ britney_proxy/britney/tests/test_process_reports.py 2014-10-31 16:39:46 +0000 |
589 | @@ -0,0 +1,112 @@ |
590 | +# Ubuntu CI Engine |
591 | +# Copyright 2014 Canonical Ltd. |
592 | + |
593 | +# This program is free software: you can redistribute it and/or modify it |
594 | +# under the terms of the GNU Affero General Public License version 3, as |
595 | +# published by the Free Software Foundation. |
596 | + |
597 | +# This program is distributed in the hope that it will be useful, but |
598 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
599 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
600 | +# PURPOSE. See the GNU Affero General Public License for more details. |
601 | + |
602 | +# You should have received a copy of the GNU Affero General Public License |
603 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
604 | + |
605 | +from cStringIO import StringIO |
606 | +import os |
607 | +import unittest |
608 | + |
609 | + |
610 | +from ucitests import fixtures as fixtures |
611 | + |
612 | + |
613 | +from britney import process_reports |
614 | + |
615 | + |
616 | +class TestBritneyReportHandlerErrors(unittest.TestCase): |
617 | + |
618 | + def setUp(self): |
619 | + super(TestBritneyReportHandlerErrors, self).setUp() |
620 | + self.handler = process_reports.BritneyReportHandler('unused') |
621 | + |
622 | + def assertError(self, exc_class, exc_msg, request): |
623 | + with self.assertRaises(exc_class) as cm: |
624 | + self.handler.handle(request) |
625 | + self.assertEqual(exc_msg, cm.exception.message) |
626 | + |
627 | + def test_empty_message(self): |
628 | + self.assertError(KeyError, 'result_at', dict()) |
629 | + |
630 | + def test_wrong_prefix(self): |
631 | + self.assertError(IndexError, 'list index out of range', |
632 | + dict(result_at='xxx')) |
633 | + |
634 | + def test_wrong_date(self): |
635 | + self.assertError(ValueError, 'need more than 1 value to unpack', |
636 | + dict(result_at='trusty/amd64/libp/libpng/xxx', |
637 | + britney_result='trusty amd64 libpng')) |
638 | + |
639 | + def test_empty_result(self): |
640 | + self.assertError( |
641 | + ValueError, 'need more than 0 values to unpack', |
642 | + dict(result_at='trusty/amd64/libp/libpng/yymmdd_hhmmss', |
643 | + britney_result='')) |
644 | + |
645 | + |
646 | +class TestBritneyReportHandler(unittest.TestCase): |
647 | + |
648 | + def setUp(self): |
649 | + super(TestBritneyReportHandler, self).setUp() |
650 | + fixtures.set_uniq_cwd(self) |
651 | + prefix = 'trusty/amd64/' |
652 | + self.handler = process_reports.BritneyReportHandler(prefix) |
653 | + |
654 | + def test_result_file_created(self): |
655 | + result = 'trusty amd64 libpng' |
656 | + self.handler.handle( |
657 | + dict(result_at='trusty/amd64/libp/libpng/yymmdd_hhmmss', |
658 | + britney_result=result)) |
659 | + result_path = 'trusty/amd64/trusty_amd64_libpng_yymmdd-hhmmss.result' |
660 | + self.assertTrue(os.path.exists(result_path), |
661 | + '{} is not there'.format(result_path)) |
662 | + with open(result_path) as f: |
663 | + self.assertEqual(result, f.read()) |
664 | + |
665 | + |
666 | +class TestCliRun(unittest.TestCase): |
667 | + "Smoke blackbox tests.""" |
668 | + |
669 | + def setUp(self): |
670 | + super(TestCliRun, self).setUp() |
671 | + self.out = StringIO() |
672 | + self.err = StringIO() |
673 | + |
674 | + def test_run_no_args_errors(self): |
675 | + with self.assertRaises(SystemExit): |
676 | + process_reports.cli_run([], self.out, self.err) |
677 | + |
678 | + |
679 | +class TestOptionParsing(unittest.TestCase): |
680 | + |
681 | + def setUp(self): |
682 | + super(TestOptionParsing, self).setUp() |
683 | + self.out = StringIO() |
684 | + self.err = StringIO() |
685 | + |
686 | + def parse_args(self, args): |
687 | + ns = process_reports.ProcessReportArgParser().parse_args( |
688 | + args, self.out, self.err) |
689 | + return ns |
690 | + |
691 | + def test_default_values(self): |
692 | + ns = self.parse_args(['reports', 'output']) |
693 | + self.assertEqual('output', ns.output) |
694 | + self.assertEqual('reports', ns.report) |
695 | + self.assertEqual(1, ns.nb_reports) |
696 | + self.assertEqual('', self.out.getvalue()) |
697 | + self.assertEqual('', self.err.getvalue()) |
698 | + |
699 | + def test_nb_reports(self): |
700 | + ns = self.parse_args(['reports', 'output', '-n0']) |
701 | + self.assertEqual(0, ns.nb_reports) |
FAILED: Continuous integration, rev:908 s-jenkins. ubuntu- ci:8080/ job/uci- engine- ci/1652/
http://
Executed test runs:
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/uci- engine- ci/1652/ rebuild
http://