Merge lp:~vila/uci-engine/britney-results into lp:uci-engine

Proposed by Vincent Ladeuil
Status: Merged
Approved by: Vincent Ladeuil
Approved revision: 828
Merged at revision: 825
Proposed branch: lp:~vila/uci-engine/britney-results
Merge into: lp:uci-engine
Prerequisite: lp:~vila/uci-engine/data-store-fix
Diff against target: 599 lines (+370/-48)
9 files modified
britney_proxy/britney/process_requests.py (+15/-28)
britney_proxy/britney/process_results.py (+153/-5)
britney_proxy/britney/queues.py (+34/-0)
britney_proxy/britney/tests/test_process_results.py (+94/-0)
ci-utils/ci_utils/testing/fixtures.py (+4/-0)
ci-utils/ci_utils/tests/test_fixtures.py (+5/-0)
juju-deployer/britney-proxy.yaml.tmpl (+4/-2)
test_runner/tstrun/run_worker.py (+4/-1)
tests/test_britney.py (+57/-12)
To merge this branch: bzr merge lp:~vila/uci-engine/britney-results
Reviewer Review Type Date Requested Status
Martin Pitt (community) Needs Fixing
PS Jenkins bot (community) continuous-integration Approve
Evan (community) Approve
Review via email: mp+236909@code.launchpad.net

Commit message

britney results processing.

Description of the change

This implement the britney result queue handling and comes with an end-to-end test that covers:
- britney request published to the queue,
- request translated and forwarded to the test runner,
- test runner processing the test request,
- britney test result acquired from the test runner and put in the britney swift container

I.e. the uci-engine part for supporting britney is done \o/

Many (small) bits are still missing and several FIXMEs introduced here already correspond to planned tasks.

The test runner results copy into the britney swift container may (ok, will ;) need adjustments but the heavy wiring work is done and further MPs will be smaller.

I'll better document how this is designed in another MP, here is a rough sketch:
- the 'britney.requests' queue is handled by the ci-airline-for-britney-requests service and forward translated requests to the test runner,
- the test runner does its job and send progress/completion messages to the 'britney.results' queue
- the 'britney.results' queue is handled by the ci-airline-for-britney-results service which copy the test results into the britney swift container which has a specific layou.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:826
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1511/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1511/rebuild

review: Approve (continuous-integration)
Revision history for this message
Evan (ev) wrote :

Looks good. Comments inline, but nothing that cannot be fixed with a follow-up.

review: Approve
Revision history for this message
Vincent Ladeuil (vila) wrote :

Replied inline, yes, follow ups are planned to address the FIXMEs.

And thanks again for the lightbulb about dead letter ;)

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:827
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1514/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1514/rebuild

review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:828
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1515/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1515/rebuild

review: Approve (continuous-integration)
Revision history for this message
Evan (ev) :
Revision history for this message
Ubuntu CI Bot (uci-bot) wrote :
Download full text (127.0 KiB)

The attempt to merge lp:~vila/uci-engine/britney-results 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...
Checking juju status
Private PPAs: disabled
Preparing local branch upload...
Uploading local branch, fingerprint efec3cf7de7c720c561628535b5fef0c01543849
Building charm: lander
Building charm: wsgi-app
Building charm: rabbitmq-worker
Building charm: chroot-builder
Building charm: key-secret-subordinate
Building charm: webui
Building charm: system-image-server
Installing keys from bzr+ssh://bazaar.launchpad.net/~ci-engineering-private/+junk/ci-airline-dev-keys/
Running juju-deployer -v -c /tmp/tmpY4DSzB/deployer/branch-source-builder.yaml -c /tmp/tmpY4DSzB/deployer/britney-proxy.yaml -c /tmp/tmpY4DSzB/deployer/gatekeeper.yaml -c /tmp/tmpY4DSzB/deployer/image-builder.yaml -c /tmp/tmpY4DSzB/deployer/lander.yaml -c /tmp/tmpY4DSzB/deployer/nf-stats-service.yaml -c /tmp/tmpY4DSzB/deployer/ppa-creator.yaml -c /tmp/tmpY4DSzB/deployer/publisher.yaml -c /tmp/tmpY4DSzB/deployer/relations.yaml -c /tmp/tmpY4DSzB/deployer/test-runner.yaml -c /tmp/tmpY4DSzB/deployer/ticket-system.yaml -c /tmp/tmpY4DSzB/deployer/validator.yaml -c /tmp/tmpY4DSzB/deployer/webui.yaml ci-airline
Tests running...
ci-utils.ci_utils.tests.test_amqp.TestAMQP.testConnectFailed ... OK (0.002 secs)
ci-utils.ci_utils.tests.test_amqp.TestAMQP.testProcessQueue ... OK (0.002 secs)
ci-utils.ci_utils.tests.test_amqp.TestAMQP.testRunForever ... OK (0.102 secs)
ci-utils.ci_utils.tests.test_amqp.TestAMQP.testSent ... OK (0.003 secs)
ci-utils.ci_utils.tests.test_amqp.TestProgressTrigger.testProgress ... OK (0.001 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testCancel ... OK (0.105 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testNoQueue ... OK (0.004 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testNoTicket ... OK (0.005 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testOnMessageCalledProcessError ... OK (0.002 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testOnMessageFail ... OK (0.002 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testOnMessageKilled ... OK (0.002 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testOnMessageSimple ... OK (0.002 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testOnMessageUnexpected ... OK (0.003 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testSaveLastRun ... OK (0.009 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestTimer.testCBRuns ... OK (0.021 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestTimer.testCanCancel ... OK (0.001 secs)
ci-utils.ci_utils.tests.test_data_store.TestDataStoreConfig.test_invalid_auth_config ... OK (0.000 secs)
ci-utils.ci_utils.tests.test_data_store.TestDataStoreConfig.test_valid_auth_config ... OK (0.000 secs)
ci-utils.ci_utils.tests.test_data_store.TestDataStoreFileName.test_abspath ... OK...

Revision history for this message
Vincent Ladeuil (vila) wrote :
Download full text (6.6 KiB)

======================================================================
ERROR: tests.test_cli.CliTest.test_create_ticket
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/tarmac/branch.aE0WBg/bin/../tests/test_cli.py", line 70, in test_create_ticket
    num_tickets = self.get_tickets_count()
  File "/tmp/tarmac/branch.aE0WBg/bin/../tests/test_cli.py", line 47, in get_tickets_count
    response.raise_for_status()
  File "/dev/shm/venv-DbmfiI/local/lib/python2.7/site-packages/requests/models.py", line 773, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
HTTPError: 500 Server Error: INTERNAL SERVER ERROR
======================================================================
ERROR: tests.test_cli.CliTest.test_update_ticket_attributes
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/tarmac/branch.aE0WBg/bin/../tests/test_cli.py", line 93, in test_update_ticket_attributes
    num_tickets = self.get_tickets_count()
  File "/tmp/tarmac/branch.aE0WBg/bin/../tests/test_cli.py", line 47, in get_tickets_count
    response.raise_for_status()
  File "/dev/shm/venv-DbmfiI/local/lib/python2.7/site-packages/requests/models.py", line 773, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
HTTPError: 500 Server Error: INTERNAL SERVER ERROR
======================================================================
ERROR: tests.test_cli.CliTest.test_update_ticket_new_sources
----------------------------------------------------------------------
Traceback (most recent call last):
  File "build/bdist.linux-x86_64/egg/mock.py", line 1201, in patched
    return func(*args, **keywargs)
  File "/tmp/tarmac/branch.aE0WBg/bin/../tests/test_cli.py", line 118, in test_update_ticket_new_sources
    num_tickets = self.get_tickets_count()
  File "/tmp/tarmac/branch.aE0WBg/bin/../tests/test_cli.py", line 47, in get_tickets_count
    response.raise_for_status()
  File "/dev/shm/venv-DbmfiI/local/lib/python2.7/site-packages/requests/models.py", line 773, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
HTTPError: 500 Server Error: INTERNAL SERVER ERROR
======================================================================
FAIL: tests.test_integration.IntegrationTest.test_create_ticket_via_cli
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/tarmac/branch.aE0WBg/bin/../tests/test_integration.py", line 86, in test_create_ticket_via_cli
    ticket_id = self.create_new_ticket(ticket_title, ci_url)
  File "/tmp/tarmac/branch.aE0WBg/bin/../tests/test_integration.py", line 74, in create_new_ticket
    0, proc.returncode, '{} failed:\n{}'.format(args, out))
  File "/usr/lib/python2.7/unittest/case.py", line 515, in assertEqual
    assertion_func(first, second, msg=msg)
  File "/usr/lib/python2.7/unittest/case.py", line 508, in _baseAssertEqual
    raise self.failureException(msg)
AssertionError: ('python', '/tmp/tarmac/branch.aE0WBg/tests/../cli/ubuntu-ci', '-u', 'http://10.0.3.253', 'create_ticket', '-t', 'Bazinga!', '-d'...

Read more...

Revision history for this message
Ubuntu CI Bot (uci-bot) wrote :
Download full text (51.6 KiB)

The attempt to merge lp:~vila/uci-engine/britney-results 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-10-03 12:33:34 INFO juju.cmd supercommand.go:37 running jujud [1.20.8.1-precise-amd64 gc]
2014-10-03 12:33:34 DEBUG juju.agent agent.go:377 read agent config, format "1.18"
2014-10-03 12:33:34 INFO juju.jujud unit.go:78 unit agent unit-ci-airline-ts-postgres-0 start (1.20.8.1-precise-amd64 [gc])
2014-10-03 12:33:34 INFO juju.worker runner.go:260 start "api"
2014-10-03 12:33:34 INFO juju.state.api apiclient.go:242 dialing "wss://10.0.3.1:17070/"
2014-10-03 12:33:34 INFO juju.state.api apiclient.go:176 connection established to "wss://10.0.3.1:17070/"
2014-10-03 12:33:35 INFO juju.state.api apiclient.go:242 dialing "wss://10.0.3.1:17070/"
2014-10-03 12:33:35 INFO juju.state.api apiclient.go:176 connection established to "wss://10.0.3.1:17070/"
2014-10-03 12:33:37 INFO juju.state.api apiclient.go:242 dialing "wss://10.0.3.1:17070/"
2014-10-03 12:33:37 INFO juju.state.api apiclient.go:176 connection established to "wss://10.0.3.1:17070/"
2014-10-03 12:33:38 INFO juju.worker runner.go:260 start "upgrader"
2014-10-03 12:33:38 INFO juju.worker runner.go:260 start "logger"
2014-10-03 12:33:38 DEBUG juju.worker.logger logger.go:35 initial log config: "<root>=DEBUG"
2014-10-03 12:33:38 INFO juju.worker runner.go:260 start "uniter"
2014-10-03 12:33:38 DEBUG juju.worker.logger logger.go:60 logger setup
2014-10-03 12:33:38 INFO juju.worker runner.go:260 start "apiaddressupdater"
2014-10-03 12:33:38 INFO juju.worker runner.go:260 start "rsyslog"
2014-10-03 12:33:38 DEBUG juju.worker.rsyslog worker.go:75 starting rsyslog worker mode 1 for "unit-ci-airline-ts-postgres-0" "tarmac-local"
2014-10-03 12:33:38 DEBUG juju.worker.logger logger.go:45 reconfiguring logging from "<root>=DEBUG" to "<root>=WARNING;unit=DEBUG"
2014-10-03 12:33:59 INFO juju-log Installing ['python-psycopg2', 'python-jinja2'] with options: ['--option=Dpkg::Options::=--force-confold']
2014-10-03 12:34:29 INFO install Reading package lists...
2014-10-03 12:34:31 INFO install Building dependency tree...
2014-10-03 12:34:31 INFO install Reading state information...
2014-10-03 12:34:32 INFO install The following extra packages will be installed:
2014-10-03 12:34:32 INFO install libpq5 python-egenix-mxdatetime python-egenix-mxtools python-markupsafe
2014-10-03 12:34:32 INFO install Suggested packages:
2014-10-03 12:34:32 INFO install python-egenix-mxdatetime-dbg python-egenix-mxdatetime-doc
2014-10-03 12:34:32 INFO install python-egenix-mxtools-dbg python-egenix-mxtools-doc python-jinja2-doc
2014-10-03 12:34:32 INFO install python-psycopg2-doc
2014-10-03 12:34:32 INFO install The following NEW packages will be installed:
2014-10-03 12:34:32 INFO install libpq5 python-egenix-mxdatetime python-egenix-mxtools python-jinja2
2014-10-03 12:34:32 INFO install ...

Revision history for this message
Martin Pitt (pitti) wrote :

Thanks! This already happened (national holiday yesterday), but I still have some comments and questions.

review: Needs Fixing
Revision history for this message
Vincent Ladeuil (vila) wrote :

Replied inline for completing the discussions on IRC.

Revision history for this message
Vincent Ladeuil (vila) wrote :

Yet Another Uncommit Comment. This one was added during the review discussion :-/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'britney_proxy/britney/process_requests.py'
2--- britney_proxy/britney/process_requests.py 2014-09-25 14:19:29 +0000
3+++ britney_proxy/britney/process_requests.py 2014-10-03 08:44:06 +0000
4@@ -18,9 +18,11 @@
5
6 import json
7 import logging
8+import sys
9 import uuid
10
11
12+from britney import queues
13 from ci_utils import (
14 amqp_utils,
15 dump_stack,
16@@ -69,39 +71,29 @@
17 series = params['series']
18 architecture = params['architecture']
19 package = params['package']
20+ # The output queue can be overridden for a given request (for tests and
21+ # debug purposes).
22+ output_queue = params.get('output_queue', self.output_queue)
23+ report_queue = params.get('report_queue', None)
24 # Only amd64 architecture is supported for now, everything else is
25 # dropped on the floor as we can't send feedback to the user at this
26- # point ( the data flow is: britney.requests -> test_runner ->
27+ # point (the data flow is: britney.requests -> test_runner ->
28 # britney.results). See
29 # https://app.asana.com/0/8312550429058/12699749556376 'reporting test
30 # requests errors'
31 if architecture == 'amd64':
32 params = dict(ticket_id=str(uuid.uuid1()),
33- progress_trigger=self.output_queue,
34+ progress_trigger=output_queue,
35 series=series,
36 image_id=self.get_image_id(series),
37 ppa_list=[],
38 package_list=[package])
39+ if report_queue is not None:
40+ params['report_queue'] = report_queue
41 self.test_runner_queue.publish(params)
42
43
44-class TestRunnerQueue(object):
45- """A proxy to the queue libray.
46-
47- This implementation targets cit_utils/amqp_utils but can be replaced for
48- tests.
49-
50- """
51-
52- def __init__(self, queue_name):
53- self.queue_name = queue_name
54-
55- def publish(self, msg):
56- amqp_utils.send(self.queue_name, json.dumps(msg),
57- raise_errors=True)
58-
59-
60-class BritneyQueue(object):
61+class BritneyRequestsQueue(object):
62 """The queue receiving the britney test requests.
63
64 The requests are forwarded to the request handler.
65@@ -132,17 +124,12 @@
66
67 if __name__ == '__main__':
68 dump_stack.install_stack_dump_signal()
69+ logging.root.setLevel(logging.INFO)
70 logging.info('Waiting for messages. ^C to exit.')
71- tr_queue = TestRunnerQueue(amqp_utils.TEST_RUNNER_QUEUE)
72+ tr_queue = queues.PublisherQueue(amqp_utils.TEST_RUNNER_QUEUE)
73 request_handler = BritneyRequestHandler(tr_queue, 'britney.results')
74 # Build the britney requests queue subscribing to the rabbit queue and
75 # proving the handler that will forward to the test runner queue.
76- queue = BritneyQueue('britney.requests', request_handler)
77+ queue = BritneyRequestsQueue('britney.requests', request_handler)
78 queue.consume()
79-
80-
81-# MISSINGTEST: Integration running a simple test (precise/amd64/libpng) under
82-# ./tests/test_britney.py (will require a dedicated fixture to not run on
83-# tarmac :-/ (which raises the issue of running all deployments on tarmac))
84-# 'process_results.py' only needs to implement a sink: just consume the
85-# messages without touching them.
86+ sys.exit(0)
87
88=== modified file 'britney_proxy/britney/process_results.py'
89--- britney_proxy/britney/process_results.py 2014-09-24 14:44:55 +0000
90+++ britney_proxy/britney/process_results.py 2014-10-03 08:44:06 +0000
91@@ -15,19 +15,167 @@
92 # along with this program. If not, see <http://www.gnu.org/licenses/>.
93 from __future__ import unicode_literals
94
95+
96+import datetime
97+import json
98 import logging
99+import os
100 import sys
101-import time
102-
103-
104+
105+
106+from britney import queues
107 from ci_utils import (
108+ amqp_utils,
109+ data_store,
110 dump_stack,
111+ unit_config,
112 )
113
114
115+class BritneyTestRunnerArtifactMapper(object):
116+
117+ def __init__(self, release, architecture, package, time_stamp, host_name):
118+ if package.startswith('lib'):
119+ prefix = package[0:4]
120+ else:
121+ prefix = package[0]
122+ self.dir_name = os.path.join(release, architecture, prefix, package,
123+ '{}_{}'.format(time_stamp, host_name))
124+
125+ def get_artifact_name(self, name):
126+ return os.path.join(self.dir_name, name)
127+
128+
129+# FIXME: We do store *all* britney test results in a single swift
130+# container. This won't scale but we'll revisit the layout when we get a better
131+# understanding of the britney needs (and numbers !) -- vila 2014-10-03
132+class BritneyDataStore(data_store.DataStore):
133+
134+ def file_name(self, filename):
135+ # Allow paths to be used as object names in the container by overriding
136+ # the filtering in the base class.
137+ return filename
138+
139+
140+class BritneyResultHandler(object):
141+ """Process a britney test request.
142+
143+ This is fed by messages received on a queue or directly for tests by
144+ calling the ``handle()`` method.
145+ """
146+
147+ def __init__(self, britney_data_store, _tr_data_store=None,
148+ _time_stamp=None):
149+ """Britney results handler.
150+
151+ :param britney_data_store: The data store where the test results are
152+ stored for britney.
153+
154+ :param _tr_data_store: The data store containing the test results.
155+ This is intended to be used by tests, the normal use case is to
156+ retrieve the test runner data store from the request parameter in
157+ the received message.
158+
159+ :param _time_stamp: The date and time the test result is
160+ processed. This is intended to be used by tests and default to
161+ utcnow().
162+
163+ """
164+ self.britney_data_store = britney_data_store
165+ self.tr_data_store = _tr_data_store
166+ self._time_stamp = _time_stamp
167+
168+ def handle_progress(self, params):
169+ # We just ignore the progress messages
170+ pass
171+
172+ def copy_artifact(self, name):
173+ # FIXME: If swift becomes unavailable we can't acquire the test results
174+ # from the test runner swift container nor can we upload them to the
175+ # britney container. It's quite unlikely though that swift becomes
176+ # unavailable *after* the test runner processed the request so just
177+ # logging an exception should be enough. -- vila 2014-10-02
178+ value = self.tr_data_store.get_file(name)
179+ target = self.mapper.get_artifact_name(name)
180+ self.britney_data_store.put_file(target, value, 'text/plain')
181+
182+ def handle_result(self, params):
183+ # Copy the produced results from the test request swift container into
184+ # the britney swift container.
185+ request = params['request']
186+ ticket_id = request['ticket_id']
187+ series = request['series']
188+ # FIXME: Needs to be supported by the test runner -- vila 2014-10-01
189+ architecture = 'amd64'
190+ if self.tr_data_store is None:
191+ self.tr_data_store = data_store.create_for_ticket(
192+ ticket_id, unit_config.get_auth_config())
193+ time_stamp = self._time_stamp
194+ if time_stamp is None:
195+ time_stamp = datetime.datetime.utcnow().strftime('%Y%m%d_%H%M%S')
196+ package = request['package_list'][0]
197+ self.mapper = BritneyTestRunnerArtifactMapper(
198+ series, architecture, package, time_stamp,
199+ # uci-engine testbed hostnames are meaningless, so we just use a
200+ # generic name here.
201+ 'uci-testbed')
202+ self.copy_artifact('{}.log'.format(package))
203+ # FIXME: We could delete the test runner swift container at that point
204+ # to reduce cluttering swift -- vila 2014-10-02
205+ report_queue = request.get('report_queue', None)
206+ if report_queue is not None:
207+ # Publish a message to report where the results have been stored
208+ queue = queues.PublisherQueue(report_queue)
209+ msg = dict(result_at=self.mapper.dir_name)
210+ queue.publish(msg)
211+
212+ def handle(self, params):
213+ state = params['state']
214+ if state == 'STATUS':
215+ self.handle_progress(params)
216+ elif state == 'COMPLETED':
217+ self.handle_result(params)
218+ else:
219+ # This indicates a programming error in the test runner producing
220+ # an unexpected message
221+ logging.error('Unable to handle {}'.format(params))
222+ # FIXME: properly testing the above will require a logger object
223+ # for the handler that can be overriden by tests -- vila 2014-09-30
224+
225+
226+class BritneyResultsQueue(object):
227+ """The queue receiving the test runner results for the britney requests."""
228+
229+ def __init__(self, queue_name, request_handler):
230+ """Britney test results queue.
231+
232+ :param queue_name: The queue name where the test results are received.
233+
234+ :param request_handler: The handler that processes the results.
235+ """
236+ self.queue_name = queue_name
237+ self.request_handler = request_handler
238+
239+ def consume(self):
240+ conf = amqp_utils.get_config()
241+ # Process all messages and delete the queue when done
242+ amqp_utils.process_queue(conf, self.queue_name, self.handle_request,
243+ delete=True)
244+
245+ def handle_request(self, msg):
246+ body = json.loads(msg.body)
247+ ret = self.request_handler.handle(body)
248+ msg.channel.basic_ack(msg.delivery_tag)
249+ return ret
250+
251+
252 if __name__ == '__main__':
253 dump_stack.install_stack_dump_signal()
254+ logging.root.setLevel(logging.INFO)
255 logging.info('Waiting for messages. ^C to exit.')
256- while True:
257- time.sleep(10)
258+ britney_data_store = BritneyDataStore(
259+ 'britney.results', unit_config.get_auth_config(), public=True)
260+ request_handler = BritneyResultHandler(britney_data_store)
261+ queue = BritneyResultsQueue('britney.results', request_handler)
262+ queue.consume()
263 sys.exit(0)
264
265=== added file 'britney_proxy/britney/queues.py'
266--- britney_proxy/britney/queues.py 1970-01-01 00:00:00 +0000
267+++ britney_proxy/britney/queues.py 2014-10-03 08:44:06 +0000
268@@ -0,0 +1,34 @@
269+# Ubuntu CI Engine
270+# Copyright 2014 Canonical Ltd.
271+
272+# This program is free software: you can redistribute it and/or modify it
273+# under the terms of the GNU Affero General Public License version 3, as
274+# published by the Free Software Foundation.
275+
276+# This program is distributed in the hope that it will be useful, but
277+# WITHOUT ANY WARRANTY; without even the implied warranties of
278+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
279+# PURPOSE. See the GNU Affero General Public License for more details.
280+
281+# You should have received a copy of the GNU Affero General Public License
282+# along with this program. If not, see <http://www.gnu.org/licenses/>.
283+
284+import json
285+
286+
287+from ci_utils import amqp_utils
288+
289+
290+class PublisherQueue(object):
291+ """A proxy to the queue library.
292+
293+ This implementation targets ci_utils/amqp_utils but can be replaced for
294+ tests.
295+ """
296+
297+ def __init__(self, queue_name):
298+ self.queue_name = queue_name
299+
300+ def publish(self, msg):
301+ amqp_utils.send(self.queue_name, json.dumps(msg),
302+ raise_errors=True)
303
304=== added file 'britney_proxy/britney/tests/test_process_results.py'
305--- britney_proxy/britney/tests/test_process_results.py 1970-01-01 00:00:00 +0000
306+++ britney_proxy/britney/tests/test_process_results.py 2014-10-03 08:44:06 +0000
307@@ -0,0 +1,94 @@
308+# Ubuntu CI Engine
309+# Copyright 2014 Canonical Ltd.
310+
311+# This program is free software: you can redistribute it and/or modify it
312+# under the terms of the GNU Affero General Public License version 3, as
313+# published by the Free Software Foundation.
314+
315+# This program is distributed in the hope that it will be useful, but
316+# WITHOUT ANY WARRANTY; without even the implied warranties of
317+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
318+# PURPOSE. See the GNU Affero General Public License for more details.
319+
320+# You should have received a copy of the GNU Affero General Public License
321+# along with this program. If not, see <http://www.gnu.org/licenses/>.
322+
323+import os
324+import unittest
325+
326+
327+from ucitests import fixtures as uci_fixtures
328+
329+
330+from britney import process_results
331+from ci_utils.testing import (
332+ features,
333+ fixtures,
334+)
335+
336+
337+class TestBritneyTestRunnerArtifactMapper(unittest.TestCase):
338+
339+ def setUp(self):
340+ super(TestBritneyTestRunnerArtifactMapper, self).setUp()
341+ # Set some defaults to make the tests less verbose
342+ self.release = 'utopic'
343+ self.architecture = 'amd64'
344+ self.time_stamp = '20141001_000000'
345+ self.host_name = 'localhost'
346+
347+ def get_mapper(self, package):
348+ mapper = process_results.BritneyTestRunnerArtifactMapper(
349+ self.release, self.architecture, package, self.time_stamp,
350+ self.host_name)
351+ return mapper
352+
353+ def test_package_name(self):
354+ mapper = self.get_mapper('hello')
355+ self.assertEquals(
356+ 'utopic/amd64/h/hello/20141001_000000_localhost/hello.log',
357+ mapper.get_artifact_name('hello.log'))
358+
359+ def test_lib_package_name(self):
360+ mapper = self.get_mapper('libpng')
361+ self.assertEquals(
362+ 'utopic/amd64/libp/libpng/20141001_000000_localhost/libpng.log',
363+ mapper.get_artifact_name('libpng.log'))
364+
365+
366+# FIXME: This works around https://app.asana.com/0/8499154990155/14496314215756
367+# 'unit_config as a fixture for tests that requires it' -- vila 2014-09-25
368+@features.requires(features.nova_creds)
369+class TestBritneyRequestHandler(unittest.TestCase):
370+
371+ def setUp(self):
372+ super(TestBritneyRequestHandler, self).setUp()
373+ uci_fixtures.set_uniq_cwd(self)
374+ self.tr_ds_path = os.path.join(self.uniq_dir, 'tr_data_store')
375+ self.tr_data_store = fixtures.FakeDataStore(self.tr_ds_path)
376+ self.britney_ds_path = os.path.join(self.uniq_dir,
377+ 'britney_data_store')
378+ self.britney_data_store = fixtures.FakeDataStore(self.britney_ds_path)
379+ self.handler = process_results.BritneyResultHandler(
380+ self.britney_data_store,
381+ _tr_data_store=self.tr_data_store,
382+ _time_stamp='20141001_000000')
383+
384+ def test_status_message(self):
385+ params = dict(state='STATUS')
386+ # Since the handler does nothing, this is mostly a smoke test
387+ self.handler.handle(params)
388+
389+ def test_completed_message(self):
390+ request = dict(ticket_id='not-used', series='utopic',
391+ package_list=['fake'])
392+ fake_content = 'fake.log content\n'
393+ fake_url = self.tr_data_store.put_file('fake.log', fake_content,
394+ 'text/plain')
395+ params = dict(state='COMPLETED', request=request,
396+ artifacts=[dict(name='fake.log', reference=fake_url,
397+ type='LOGS')])
398+ self.handler.handle(params)
399+ britney_name = self.handler.mapper.get_artifact_name('fake.log')
400+ actual_content = self.britney_data_store.get_file(britney_name)
401+ self.assertEquals(fake_content, actual_content)
402
403=== modified file 'ci-utils/ci_utils/testing/fixtures.py'
404--- ci-utils/ci_utils/testing/fixtures.py 2014-07-03 14:43:54 +0000
405+++ ci-utils/ci_utils/testing/fixtures.py 2014-10-03 08:44:06 +0000
406@@ -56,6 +56,10 @@
407 return sorted(os.listdir(self.container_id))
408
409 def put_file(self, filename, contents, content_type=None):
410+ if os.sep in filename:
411+ base_dir = os.path.join(self.container_id,
412+ os.path.dirname(filename))
413+ os.makedirs(base_dir)
414 full_path = os.path.join(self.container_id, filename)
415 with open(full_path, 'w') as f:
416 f.write(contents)
417
418=== modified file 'ci-utils/ci_utils/tests/test_fixtures.py'
419--- ci-utils/ci_utils/tests/test_fixtures.py 2014-05-06 15:05:18 +0000
420+++ ci-utils/ci_utils/tests/test_fixtures.py 2014-10-03 08:44:06 +0000
421@@ -43,6 +43,11 @@
422 self.assertEqual(['foo'], self.ds.list_files())
423 self.assertEqual('foo content\n', self.ds.get_file('foo'))
424
425+ def test_put_file_with_slashes(self):
426+ url = self.ds.put_file('foo/bar/baz', 'baz content\n')
427+ self.assertTrue(url.endswith(os.path.join(self.ds_path,
428+ 'foo/bar/baz')))
429+
430 def test_list_files_empty(self):
431 self.assertEqual([], self.ds.list_files())
432
433
434=== modified file 'juju-deployer/britney-proxy.yaml.tmpl'
435--- juju-deployer/britney-proxy.yaml.tmpl 2014-10-01 08:55:19 +0000
436+++ juju-deployer/britney-proxy.yaml.tmpl 2014-10-03 08:44:06 +0000
437@@ -10,9 +10,10 @@
438
439 # Until we do: https://app.asana.com/0/8312550429058/16178663570516 'minimal
440 # images for britney', we use the ones defined in ci_utils/testing/features
441-# which requires ucitests
442+# which requires ucitests, python-swiftclient and python-novaclient (and
443+# indirectly mock and gnupg :-/)
444
445- packages: "python-uci-tests"
446+ packages: "python-uci-tests python-swiftclient python-novaclient python-mock python-gnupg"
447 unit-config: include-base64://configs/unit_config.yaml
448 uid: ubuntu
449 gid: ubuntu
450@@ -26,6 +27,7 @@
451 main: ./run-python ./britney_proxy/britney/process_results.py
452 current_code: ${CI_PAYLOAD_URL}
453 available_code: ${CI_PAYLOAD_URL}
454+ packages: "python-swiftclient"
455 unit-config: include-base64://configs/unit_config.yaml
456 uid: ubuntu
457 gid: ubuntu
458
459=== modified file 'test_runner/tstrun/run_worker.py'
460--- test_runner/tstrun/run_worker.py 2014-09-23 14:54:03 +0000
461+++ test_runner/tstrun/run_worker.py 2014-10-03 08:44:06 +0000
462@@ -232,7 +232,10 @@
463 package_list = params['package_list']
464 data_store = self._create_data_store(params['ticket_id'])
465
466- results = dict(artifacts=[])
467+ # The request parameters are forwarded to the rabbit worker processing
468+ # the status/result queue to enable unidirectional communication
469+ # between the requester and the ultimate receiver.
470+ results = dict(artifacts=[], request=params)
471
472 # The tests will succeed unless they fail ;)
473 notify = amqp_utils.progress_completed
474
475=== modified file 'tests/test_britney.py'
476--- tests/test_britney.py 2014-09-24 16:44:08 +0000
477+++ tests/test_britney.py 2014-10-03 08:44:06 +0000
478@@ -15,6 +15,7 @@
479 # along with this program. If not, see <http://www.gnu.org/licenses/>.
480
481 import json
482+import os
483 import unittest
484
485
486@@ -27,6 +28,9 @@
487 )
488
489
490+from britney import process_results
491+
492+
493 class TestProcessRequests(deployers.DeployerTest):
494
495 def setUp(self):
496@@ -50,12 +54,12 @@
497 else:
498 self.test_images = features.canonistack_test_images
499
500- def drain_queue(self, queue_name):
501- self.messages = []
502+ def drain_tr_queue(self, queue_name):
503+ messages = []
504
505 def on_message(msg):
506 body = json.loads(msg.body)
507- self.messages.append(body)
508+ messages.append(body)
509 msg.channel.basic_ack(msg.delivery_tag)
510 if body.get('exit'):
511 # this will kick us out of the run-forever worker loop, relying
512@@ -65,7 +69,7 @@
513 conf = amqp_utils.get_config()
514 # Process all messages and delete the queue when done
515 amqp_utils.process_queue(conf, queue_name, on_message, delete=True)
516- return self.messages
517+ return messages
518
519 def assertStatusMsg(self, expected, msg):
520 self.assertEqual('STATUS', msg['state'])
521@@ -76,22 +80,21 @@
522 def get_image_id(self, series):
523 return self.test_images[series]
524
525- def test_process_test_request(self):
526+ def test_tr_process_test_request(self):
527 # Exercise the britney API: feed a test request on the britney requests
528 # queue and ensures the test runner receive a translated request and
529- # process it properly.
530-
531- # Until we have a proper implementation for the britney results queue,
532- # this will hijack the queue here.
533- progress_queue_name = 'britney.results'
534+ # process it properly. We force a specific result queue to avoid
535+ # involving the britney results service.
536+ result_queue_name = 'britney.test_tr_process_test_request'
537 series = 'precise'
538 architecture = 'amd64'
539 params = dict(series=series,
540 architecture=architecture,
541- package='libpng')
542+ package='libpng',
543+ output_queue=result_queue_name)
544 amqp_utils.send('britney.requests', json.dumps(params),
545 raise_errors=True)
546- msgs = self.drain_queue(progress_queue_name)
547+ msgs = self.drain_tr_queue(result_queue_name)
548 assertions.assertLength(self, 6, msgs)
549 # The test runner received the translated request
550 tr_params = msgs[0]
551@@ -111,6 +114,48 @@
552 self.assertEqual('FAILED', final['result'])
553 self.assertEqual('COMPLETED', final['state'])
554
555+ def drain_report_queue(self, queue_name):
556+
557+ messages = []
558+
559+ def on_message(msg):
560+ # We expect a single message here so we just ack it and leave the
561+ # loop
562+ body = json.loads(msg.body)
563+ messages.append(body)
564+ msg.channel.basic_ack(msg.delivery_tag)
565+ msg.channel.basic_cancel(msg.consumer_tag)
566+
567+ conf = amqp_utils.get_config()
568+ # Process all messages and delete the queue when done
569+ amqp_utils.process_queue(conf, queue_name, on_message, delete=True)
570+ return messages
571+
572+ def test_process_test_and_result_request(self):
573+ # Exercise the britney API: feed a test request on the britney requests
574+ # queue and check that the britney result is processed.
575+ report_queue_name = 'test-britney.results'
576+ series = 'precise'
577+ architecture = 'amd64'
578+ params = dict(series=series,
579+ architecture=architecture,
580+ package='libpng',
581+ report_queue=report_queue_name)
582+ amqp_utils.send('britney.requests', json.dumps(params),
583+ raise_errors=True)
584+ msgs = self.drain_report_queue(report_queue_name)
585+ # The results are in the britney swift container
586+ assertions.assertLength(self, 1, msgs)
587+ # Check the swift container content
588+ dir_name = msgs[0]['result_at']
589+ log_file_name = os.path.join(dir_name, 'libpng.log')
590+ # We delay the import so it won't fail when the 'unit_config' file
591+ # is not available
592+ from ci_utils import unit_config
593+ britney_data_store = process_results.BritneyDataStore(
594+ 'britney.results', unit_config.get_auth_config(), public=True)
595+ self.assertIn(log_file_name, britney_data_store.list_files())
596+
597
598 if __name__ == "__main__":
599 unittest.main()

Subscribers

People subscribed via source and target branches