Merge lp:~cprov/adt-cloud-worker/adt-runs into lp:adt-cloud-worker

Proposed by Celso Providelo
Status: Merged
Merged at revision: 7
Proposed branch: lp:~cprov/adt-cloud-worker/adt-runs
Merge into: lp:adt-cloud-worker
Diff against target: 216 lines (+81/-87)
2 files modified
.adt-service.conf (+3/-2)
adt-cloud-worker.py (+78/-85)
To merge this branch: bzr merge lp:~cprov/adt-cloud-worker/adt-runs
Reviewer Review Type Date Requested Status
Francis Ginther Approve
Review via email: mp+251624@code.launchpad.net

Description of the change

Supporting nova.extra_args configuration for setting bootstack net-id (and other possible stack-dependant options) and also took the opportunity for refactoring some logic inside AdtNovaWorker consumer by passing the configuration object in. Now process & run_adt are more concise and easier to understand and test.

`adt-cloud-worker` works for libpng on trusty (using the test producer from lp:adt-request-proxy), now we have to store results on swift and call it feature complete.

Yes, you might have noticed we still have no unittests :-/

To post a comment you must log in.
Revision history for this message
Francis Ginther (fginther) wrote :

Looks good. I first thought it a mistake to leave out "# TODO: do sensible things with the exit code:", but I believe this is really a task for the result-checker.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.adt-service.conf'
2--- .adt-service.conf 2015-03-03 02:15:49 +0000
3+++ .adt-service.conf 2015-03-03 16:37:14 +0000
4@@ -12,5 +12,6 @@
5 os_username = foo
6 os_tenant_name = foo_project
7 os_password = <redacted>
8-os_auth_url = https://keystone.canonistack.canonical.com:443/v2.0/
9-os_region_name = lcy01
10+os_auth_url = http://172.20.161.138:5000/v2.0/
11+os_region_name = bot-prototype
12+extra_args = --net-id=415a0839-eb05-4e7a-907c-413c657f4bf5
13
14=== modified file 'adt-cloud-worker.py'
15--- adt-cloud-worker.py 2015-03-03 12:16:42 +0000
16+++ adt-cloud-worker.py 2015-03-03 16:37:14 +0000
17@@ -36,95 +36,102 @@
18 logger = get_logger(__name__)
19
20
21-class Worker(ConsumerMixin):
22+class AdtNovaWorker(ConsumerMixin):
23
24- def __init__(self, name, connection, queues):
25- self.name = name
26+ def __init__(self, connection, config):
27 self.connection = connection
28- self.queues = queues
29+ self.config = config
30+ self.name = self.config.get('adt', 'name')
31
32 def get_consumers(self, Consumer, channel):
33- return [Consumer(queues=self.queues, callbacks=[self.process])]
34+ """Return consumers instances for all configured queues."""
35+ exchange = kombu.Exchange("adt.exchange", type="topic")
36+ queues = [
37+ kombu.Queue(
38+ 'adt.requests.{}'.format(tag), exchange, routing_key=tag)
39+ for tag in self.config.get('adt', 'tags').split()
40+ ]
41+ return [Consumer(queues=queues, callbacks=[self.process])]
42
43 def process(self, body, message):
44+ """Process incomming test request.
45+
46+ Run requested tests and posts results to the 'adt_results' queue
47+ for later checking.
48+ """
49 logger.info('Got: {}'.format(body))
50 # TODO: body validation
51- # Ack message once it's valid or message.reject()
52 message.ack()
53-
54- result_dir = tempfile.mkdtemp(
55- prefix='adt-{}'.format(body['request_id']))
56-
57+ body['worker'] = self.name
58+ # Run requested tests safely.
59 try:
60- # what information do we need from the message?
61- adt_run_args = [
62- '--apt-source', body['package_name'],
63- '--output-dir', result_dir, # TODO: replace me
64- ]
65- if 'apt_pocket' in body:
66- adt_run_args += [
67- '--apt-pocket', body['apt_pocket'],
68- '--apt-upgrade',
69- ]
70- adt_ssh_nova_args = [
71- '--',
72- '--flavor', body['nova_flavor'],
73- '--image', body['nova_image'],
74- ]
75-
76- exit_code = run_adt(
77- adt_run_args +
78- [
79- '---',
80- 'ssh',
81- '-s', 'nova',
82- ] +
83- adt_ssh_nova_args
84- )
85-
86- # TODO: do sensible things with the exit code:
87- # 0 all tests passed
88- # 2 at least one test skipped
89- # 4 at least one test failed
90- # 6 at least one test failed and at least one test skipped
91- # 8 no tests in this package
92- # 12 erroneous package
93- # 16 testbed failure
94- # 20 other unexpected failures including bad usage
95-
96+ # Create and temporary directory for storing adt results.
97+ result_dir = tempfile.mkdtemp(
98+ prefix='adt-{}-{}'.format(self.name, body['request_id']))
99+ adt_kwargs = body.copy()
100+ adt_kwargs['result_dir'] = result_dir
101+ body['exit_code'] = self.run_adt(**adt_kwargs)
102 # TODO: tar/gzip the output directory.
103 # TODO: upload to swift instance at 'swift_container'
104-
105 except Exception as e:
106+ # Unexpected failures ...
107 logger.error(e, exc_info=True)
108+ body['exit_code'] = '100'
109 finally:
110- # build the result message
111- body['worker'] = self.name
112- body['exit_code'] = exit_code
113+ # Drop the result directory and post results
114+ shutil.rmtree(result_dir)
115 # TODO: send error logging to result-checker in the message
116-
117+ # TODO: resilience agains rabbit hiccups, otherwise the request
118+ # will be lost.
119 queue = self.connection.SimpleQueue('adt.results')
120 queue.put(body)
121 queue.close()
122- # Drop the result directory
123- # TODO: it could be possibly useful for clients.
124- shutil.rmtree(result_dir)
125-
126-
127-def run_adt(arguments):
128- """Run adt-run with the given arguments.
129-
130- Returns the exit code from adt-run.
131-
132- """
133- # TODO: We probably want something more clever here:
134- try:
135- subprocess.check_call(['adt-run'] + arguments)
136- except subprocess.CalledProcessError as e:
137- # log?
138- # TODO: filter log content to avoid leaking cloud credentials.
139- return e.returncode
140- return 0
141+
142+ def run_adt(self, **kwargs):
143+ """Run adt-run according to the given request parameters.
144+
145+ Always resets the environment with configured nova variables.
146+
147+ Returns the exit code from adt-run.
148+ """
149+ # TODO: We probably want something more clever here:
150+
151+ # what information do we need from the message?
152+ adt_run_args = [
153+ '--apt-source', kwargs['package_name'],
154+ '--output-dir', kwargs['result_dir'],
155+ ]
156+ if 'apt_pocket' in kwargs:
157+ adt_run_args += [
158+ '--apt-pocket', kwargs['apt_pocket'],
159+ '--apt-upgrade',
160+ ]
161+ adt_ssh_nova_args = [
162+ '--',
163+ '--flavor', kwargs['nova_flavor'],
164+ '--image', kwargs['nova_image'],
165+ ]
166+
167+ arguments = (
168+ adt_run_args +
169+ ['---', 'ssh', '-s', 'nova'] +
170+ adt_ssh_nova_args +
171+ self.config.get('nova', 'extra_args').split()
172+ )
173+
174+ # Setup nova environment variables based on the configuration file.
175+ for k, v in self.config.items('nova'):
176+ if not k.startswith('os_'):
177+ continue
178+ os.environ[k.upper()] = str(v)
179+
180+ try:
181+ subprocess.check_call(['adt-run'] + arguments)
182+ except subprocess.CalledProcessError as e:
183+ # log?
184+ # TODO: filter log content to avoid leaking cloud credentials.
185+ return e.returncode
186+ return 0
187
188
189 def main():
190@@ -139,25 +146,11 @@
191 # Load configuration options.
192 config = configparser.ConfigParser()
193 config.read(args.conf)
194- worker_name = config.get('adt', 'name')
195- routing_keys = config.get('adt', 'tags').split()
196 amqp_uris = config.get('amqp', 'uris').split()
197
198- # Setup nova environment variables based on the configuration file.
199- for k, v in config.items('nova'):
200- os.environ[k.upper()] = str(v)
201-
202- adt_exchange = kombu.Exchange("adt.exchange", type="topic")
203- queues = []
204- for routing_key in routing_keys:
205- queues.append(
206- kombu.Queue('adt.requests.{}'.format(routing_key) ,
207- adt_exchange, routing_key=routing_key)
208- )
209-
210 with kombu.Connection(amqp_uris) as conn:
211 try:
212- worker = Worker(worker_name, conn, queues)
213+ worker = AdtNovaWorker(conn, config)
214 worker.run()
215 except KeyboardInterrupt:
216 print('Bye!')

Subscribers

People subscribed via source and target branches