Merge lp:~sbaldassin/qakit/ust_worker into lp:qakit

Proposed by Santiago Baldassin
Status: Needs review
Proposed branch: lp:~sbaldassin/qakit/ust_worker
Merge into: lp:qakit
Diff against target: 429 lines (+394/-0)
7 files modified
qakit/britney/__init__.py (+19/-0)
qakit/britney/config.py (+67/-0)
qakit/britney/domains.json (+8/-0)
qakit/britney/report.py (+120/-0)
qakit/britney/utils.py (+47/-0)
qakit/britney/worker.py (+94/-0)
start_worker.py (+39/-0)
To merge this branch: bzr merge lp:~sbaldassin/qakit/ust_worker
Reviewer Review Type Date Requested Status
Allan LeSage (community) Needs Fixing
Sergio Cazzolato Needs Fixing
Review via email: mp+303564@code.launchpad.net

Commit message

Ubuntu system test worker

Description of the change

Ubuntu system test worker

To post a comment you must log in.
Revision history for this message
Sergio Cazzolato (sergio-j-cazzolato) wrote :

some minor comments inline

review: Needs Fixing
Revision history for this message
Allan LeSage (allanlesage) wrote :

Admit that this is all a bit abstract for me :) but can I ask that you move your entry-level ("main") script into your dir? Or if you insist on leaving it in the root dir would you indicate to which sub-project it belongs in its name, e.g.?

review: Needs Fixing

Unmerged revisions

152. By Santiago Baldassin

Ubuntu system test worker

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'qakit/britney'
2=== added file 'qakit/britney/__init__.py'
3--- qakit/britney/__init__.py 1970-01-01 00:00:00 +0000
4+++ qakit/britney/__init__.py 2016-08-22 14:09:13 +0000
5@@ -0,0 +1,19 @@
6+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
7+
8+#
9+# Ubuntu System Tests
10+# Copyright (C) 2016 Canonical
11+#
12+# This program is free software: you can redistribute it and/or modify
13+# it under the terms of the GNU General Public License as published by
14+# the Free Software Foundation, either version 3 of the License, or
15+# (at your option) any later version.
16+#
17+# This program is distributed in the hope that it will be useful,
18+# but WITHOUT ANY WARRANTY; without even the implied warranty of
19+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+# GNU General Public License for more details.
21+#
22+# You should have received a copy of the GNU General Public License
23+# along with this program. If not, see <http://www.gnu.org/licenses/>.
24+#
25
26=== added file 'qakit/britney/config.py'
27--- qakit/britney/config.py 1970-01-01 00:00:00 +0000
28+++ qakit/britney/config.py 2016-08-22 14:09:13 +0000
29@@ -0,0 +1,67 @@
30+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
31+
32+#
33+# Ubuntu System Tests
34+# Copyright (C) 2016 Canonical
35+#
36+# This program is free software: you can redistribute it and/or modify
37+# it under the terms of the GNU General Public License as published by
38+# the Free Software Foundation, either version 3 of the License, or
39+# (at your option) any later version.
40+#
41+# This program is distributed in the hope that it will be useful,
42+# but WITHOUT ANY WARRANTY; without even the implied warranty of
43+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
44+# GNU General Public License for more details.
45+#
46+# You should have received a copy of the GNU General Public License
47+# along with this program. If not, see <http://www.gnu.org/licenses/>.
48+#
49+
50+from ubuntu_system_tests.common.config import UbuntuSystemTestsConfig
51+
52+
53+class WorkerConfig:
54+
55+ def __init__(self):
56+ self._default = None
57+ self._practitest = None
58+ self._amqp = None
59+ self._britney = None
60+ self._swift = None
61+
62+ @property
63+ def default(self):
64+ if self._default is None:
65+ self._default = self.__get_config('default')
66+ return self._default
67+
68+ @property
69+ def practitest(self):
70+ if self._practitest is None:
71+ self._practitest = self.__get_config('practitest')
72+ return self._practitest
73+
74+ @property
75+ def amqp(self):
76+ if self._amqp is None:
77+ self._amqp = self.__get_config('amqp')
78+ return self._amqp
79+
80+ @property
81+ def britney(self):
82+ if self._britney is None:
83+ self._britney = self.__get_config('britney')
84+ return self._britney
85+
86+ @property
87+ def swift(self):
88+ if self._swift is None:
89+ self._swift = self.__get_config('swift')
90+ return self._swift
91+
92+ @staticmethod
93+ def __get_config(section):
94+ cfg = UbuntuSystemTestsConfig(section=section)
95+ cfg.get_config_from_file()
96+ return cfg
97
98=== added file 'qakit/britney/domains.json'
99--- qakit/britney/domains.json 1970-01-01 00:00:00 +0000
100+++ qakit/britney/domains.json 2016-08-22 14:09:13 +0000
101@@ -0,0 +1,8 @@
102+{
103+ "autopilot": ["63805"],
104+ "ubuntu-ui-toolkit": ["63805"],
105+ "account-plugins": ["63805"],
106+ "signon-plugin-oauth2": ["63805"],
107+ "messaging-app": ["63805"],
108+ "goget-ubuntu-touch": ["63805"]
109+}
110
111=== added file 'qakit/britney/report.py'
112--- qakit/britney/report.py 1970-01-01 00:00:00 +0000
113+++ qakit/britney/report.py 2016-08-22 14:09:13 +0000
114@@ -0,0 +1,120 @@
115+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
116+
117+#
118+# Ubuntu System Tests
119+# Copyright (C) 2016 Canonical
120+#
121+# This program is free software: you can redistribute it and/or modify
122+# it under the terms of the GNU General Public License as published by
123+# the Free Software Foundation, either version 3 of the License, or
124+# (at your option) any later version.
125+#
126+# This program is distributed in the hope that it will be useful,
127+# but WITHOUT ANY WARRANTY; without even the implied warranty of
128+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
129+# GNU General Public License for more details.
130+#
131+# You should have received a copy of the GNU General Public License
132+# along with this program. If not, see <http://www.gnu.org/licenses/>.
133+#
134+import gzip
135+import os
136+from abc import abstractmethod
137+
138+import subprocess
139+import swiftclient
140+
141+
142+class Reporter:
143+
144+ def __init__(self, output_dir):
145+ self.output_dir = output_dir
146+
147+ @abstractmethod
148+ def report(self):
149+ pass
150+
151+
152+class SwiftReporter(Reporter):
153+
154+ def __init__(self, swift_config, britney_config, output_dir):
155+ self.container = britney_config.get('container')
156+ self.expected_results_files = britney_config.get(
157+ 'expected_results_files').split(',')
158+ self.result_file = britney_config.get('result_file')
159+ self.log_file = britney_config.get('log_file')
160+ self.artifacts_file = britney_config.get('artifacts_file')
161+
162+ self.region = swift_config.get('region_name')
163+ self.auth_url = swift_config.get('auth_url')
164+ self.username = swift_config.get('username')
165+ self.tenant = swift_config.get('tenant')
166+ self.password = swift_config.get('password')
167+
168+ super().__init__(output_dir)
169+
170+ def report(self):
171+ '''
172+ Reports to swift should include three files:
173+ 1. artifacts.tar.gz
174+ 2. log.gz
175+ 3. result.tar
176+
177+ This method will connect to the swift backend and will
178+ put the files above in the container defined within the
179+ configuration file
180+
181+ '''
182+
183+ # create it if it does not exist yet
184+ swift_con = swiftclient.Connection(authurl=self.auth_url,
185+ user=self.username,
186+ key=self.password)
187+ # self.process_output_dir()
188+
189+ try:
190+ swift_con.get_container(self.container, limit=1)
191+ except swiftclient.exceptions.ClientException:
192+ swift_con.put_container(self.container, headers={
193+ 'X-Container-Read': '.rlistings,.r:*'})
194+
195+ result_file = os.path.join(self.output_dir, self.result_file)
196+ with open(result_file) as contents:
197+ swift_con.put_object(self.container,
198+ self.result_file,
199+ contents=contents.read())
200+
201+ log_file = os.path.join(self.output_dir, self.log_file)
202+ artifacts_file = os.path.join(self.output_dir, self.artifacts_file)
203+ with gzip.open(log_file) as contents:
204+ swift_con.put_object(self.container,
205+ self.log_file,
206+ contents=contents.read(),
207+ content_type='text/plain; charset=UTF-8',
208+ headers={'Content-Encoding': 'utf-8'})
209+ with gzip.open(artifacts_file) as contents:
210+ swift_con.put_object(self.container,
211+ self.artifacts_file,
212+ contents=contents)
213+ swift_con.close()
214+
215+ def process_output_dir(self):
216+ '''Post-process output directory'''
217+ results_files = list(set(os.listdir(self.output_dir)))
218+ for file in self.expected_results_files:
219+ if file not in results_files:
220+ raise RuntimeError("File {} not found within the "
221+ "result files".format(file))
222+ else:
223+ results_files.remove(file)
224+
225+ subprocess.check_call(['tar', 'cf', 'result.tar'] +
226+ self.expected_results_files,
227+ cwd=self.output_dir)
228+
229+ subprocess.check_call(['gzip', '-9',
230+ os.path.join(self.output_dir, 'log')])
231+ results_files.remove('log')
232+
233+ subprocess.check_call(['tar', '-czf', 'artifacts.tar.gz'] +
234+ results_files, cwd=self.output_dir)
235
236=== added file 'qakit/britney/utils.py'
237--- qakit/britney/utils.py 1970-01-01 00:00:00 +0000
238+++ qakit/britney/utils.py 2016-08-22 14:09:13 +0000
239@@ -0,0 +1,47 @@
240+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
241+
242+#
243+# Ubuntu System Tests
244+# Copyright (C) 2016 Canonical
245+#
246+# This program is free software: you can redistribute it and/or modify
247+# it under the terms of the GNU General Public License as published by
248+# the Free Software Foundation, either version 3 of the License, or
249+# (at your option) any later version.
250+#
251+# This program is distributed in the hope that it will be useful,
252+# but WITHOUT ANY WARRANTY; without even the implied warranty of
253+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
254+# GNU General Public License for more details.
255+#
256+# You should have received a copy of the GNU General Public License
257+# along with this program. If not, see <http://www.gnu.org/licenses/>.
258+#
259+import os
260+import json
261+
262+from launchpadlib.launchpad import Launchpad
263+
264+
265+def get_filters_for_src_pkgs(src_pkgs):
266+ britney_dir = os.path.dirname(__file__)
267+ domains_file = os.path.join(str(britney_dir), 'domains.json')
268+ with open(domains_file) as domains:
269+ domains_json = json.loads(domains.read())
270+ filters = [domains_json[package] for package in src_pkgs]
271+
272+ # Flat the lists and remove duplicates
273+ filters_list = list(set([item for sublist in filters for item in sublist]))
274+ return ','.join(filter for filter in filters_list)
275+
276+
277+def get_source_packages(message_body):
278+ msg_json = json.loads(message_body.decode('utf-8'))
279+ team_name, ppa_name = msg_json['ppa'].split('/')
280+ launchpad = Launchpad.login_anonymously('ci job', 'production')
281+ team = launchpad.people.findTeam(text=team_name)[0]
282+ archive = team.getPPAByName(name=ppa_name)
283+ src_packages = archive.getPublishedSources(status='Published')
284+ packages = list(
285+ set([package.source_package_name for package in src_packages]))
286+ return packages
287
288=== added file 'qakit/britney/worker.py'
289--- qakit/britney/worker.py 1970-01-01 00:00:00 +0000
290+++ qakit/britney/worker.py 2016-08-22 14:09:13 +0000
291@@ -0,0 +1,94 @@
292+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
293+
294+#
295+# Ubuntu System Tests
296+# Copyright (C) 2016 Canonical
297+#
298+# This program is free software: you can redistribute it and/or modify
299+# it under the terms of the GNU General Public License as published by
300+# the Free Software Foundation, either version 3 of the License, or
301+# (at your option) any later version.
302+#
303+# This program is distributed in the hope that it will be useful,
304+# but WITHOUT ANY WARRANTY; without even the implied warranty of
305+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
306+# GNU General Public License for more details.
307+#
308+# You should have received a copy of the GNU General Public License
309+# along with this program. If not, see <http://www.gnu.org/licenses/>.
310+#
311+
312+import logging
313+
314+from pika import BlockingConnection, ConnectionParameters
315+from qakit.britney.config import WorkerConfig
316+from qakit.britney.report import SwiftReporter
317+
318+from qakit.britney.utils import (
319+ get_filters_for_src_pkgs, get_source_packages)
320+from ubuntu_system_tests.host.command_line import (
321+ parse_arguments, run_system_tests, setup_system_tests)
322+from ubuntu_system_tests.host.practitest import PractitestSession
323+
324+
325+class UstWorker:
326+
327+ def __init__(self, config):
328+ self.config = config
329+
330+ def start(self):
331+ logging.info('Connecting to AMQP server: {}'.format(
332+ self.config.amqp.get('host')))
333+ connection = BlockingConnection(ConnectionParameters(
334+ host=self.config.amqp.get('host')))
335+ channel = connection.channel()
336+
337+ # TODO: Support multiple (queue, request) set in the configuration file
338+ queue = self.config.amqp.get('queue')
339+ logging.info('Subscribing to queue: {}'.format(
340+ self.config.amqp.get('queue')))
341+ channel.queue_declare(queue, durable=True, auto_delete=False)
342+ channel.basic_consume(
343+ self.process_ust_request, queue=queue, no_ack=True)
344+ channel.start_consuming()
345+
346+ def process_ust_request(self, ch, method, properties, body):
347+ '''Callback for ubuntu system test request'''
348+ # Parse the message body to get the source packages. Get the
349+ # practitests filters ids associated to the source packages
350+ # and get the tests based on the filters
351+ source_packages = get_source_packages(body)
352+ filters = get_filters_for_src_pkgs(source_packages)
353+
354+ # pt = PractitestSession(self.config.practitest.get(
355+ # '_practitest_api_token'),
356+ # self.config.practitest.get(
357+ # '_practitest_project_id'))
358+ # tests = ','.join(test for test in pt.get_tests(filters))
359+
360+ tests = 'ubuntu_system_tests.tests.test_calculator'
361+ # Setup the device with ubuntu-system-tests and run the tests
362+ setup_args = parse_arguments(['setup', tests])
363+ setup_system_tests(setup_args)
364+
365+ run_args = parse_arguments(['run', tests])
366+ run_system_tests(run_args)
367+
368+ # publish results into swift
369+ reporter = SwiftReporter(self.config.swift,
370+ self.config.britney,
371+ self.config.default.get('output_dir'))
372+ reporter.report()
373+
374+ logging.info('Acknowledging request %s' % body)
375+ logging.info(body)
376+
377+
378+def main():
379+ worker_config = WorkerConfig()
380+ worker = UstWorker(worker_config)
381+ worker.start()
382+
383+
384+if __name__ == '__main__':
385+ main()
386
387=== added file 'start_worker.py'
388--- start_worker.py 1970-01-01 00:00:00 +0000
389+++ start_worker.py 2016-08-22 14:09:13 +0000
390@@ -0,0 +1,39 @@
391+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
392+
393+#
394+# Ubuntu System Tests
395+# Copyright (C) 2016 Canonical
396+#
397+# This program is free software: you can redistribute it and/or modify
398+# it under the terms of the GNU General Public License as published by
399+# the Free Software Foundation, either version 3 of the License, or
400+# (at your option) any later version.
401+#
402+# This program is distributed in the hope that it will be useful,
403+# but WITHOUT ANY WARRANTY; without even the implied warranty of
404+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
405+# GNU General Public License for more details.
406+#
407+# You should have received a copy of the GNU General Public License
408+# along with this program. If not, see <http://www.gnu.org/licenses/>.
409+#
410+
411+import sys
412+import logging
413+from qakit.britney import worker
414+
415+
416+root = logging.getLogger()
417+root.setLevel(logging.DEBUG)
418+
419+ch = logging.StreamHandler(sys.stdout)
420+ch.setLevel(logging.INFO)
421+formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
422+ch.setFormatter(formatter)
423+root.addHandler(ch)
424+
425+def main():
426+ return worker.main()
427+
428+if __name__ == '__main__':
429+ sys.exit(main())

Subscribers

People subscribed via source and target branches