Merge lp:~viswesn/juju-ci-tools/assess-check-openstack into lp:juju-ci-tools
- assess-check-openstack
- Merge into trunk
Proposed by
viswesuwara nathan
Status: | Needs review |
---|---|
Proposed branch: | lp:~viswesn/juju-ci-tools/assess-check-openstack |
Merge into: | lp:juju-ci-tools |
Diff against target: |
402 lines (+371/-0) 4 files modified
assess_check_openstack.py (+187/-0) jujupy.py (+26/-0) tests/test_assess_check_openstack.py (+108/-0) tests/test_jujupy.py (+50/-0) |
To merge this branch: | bzr merge lp:~viswesn/juju-ci-tools/assess-check-openstack |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Christopher Lee (community) | Needs Fixing | ||
Review via email: mp+313036@code.launchpad.net |
Commit message
Description of the change
Validation of Juju validate-image and validate-tool on generating agent image and tools for Openstack cloud.
To post a comment you must log in.
- 1799. By viswesuwara nathan
-
Review comments addressed
- 1800. By viswesuwara nathan
-
Fixed couple of indentation and error messages
- 1801. By viswesuwara nathan
-
Added Unit test cases and addressed code comments
Unmerged revisions
- 1801. By viswesuwara nathan
-
Added Unit test cases and addressed code comments
- 1800. By viswesuwara nathan
-
Fixed couple of indentation and error messages
- 1799. By viswesuwara nathan
-
Review comments addressed
- 1798. By viswesuwara nathan
-
Verify juju validate-tool and validate-image for openstack cloud
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'assess_check_openstack.py' |
2 | --- assess_check_openstack.py 1970-01-01 00:00:00 +0000 |
3 | +++ assess_check_openstack.py 2017-01-06 14:05:59 +0000 |
4 | @@ -0,0 +1,187 @@ |
5 | +#!/usr/bin/env python |
6 | +""" |
7 | + assess_check_openstack.py perform juju metadata generate-image and |
8 | + generate-tools for the passed argument and does juju validation for |
9 | + images and tools using juju metadata validate-image and validate-tools |
10 | +""" |
11 | + |
12 | +from __future__ import print_function |
13 | + |
14 | +import argparse |
15 | +import logging |
16 | +import sys |
17 | +import json |
18 | +import subprocess |
19 | +import os |
20 | + |
21 | +from shutil import copyfile |
22 | + |
23 | +from deploy_stack import ( |
24 | + BootstrapManager, |
25 | + ) |
26 | +from utility import ( |
27 | + add_basic_testing_arguments, |
28 | + configure_logging, |
29 | + JujuAssertionError, |
30 | + temp_dir, |
31 | + ) |
32 | + |
33 | +__metaclass__ = type |
34 | + |
35 | +log = logging.getLogger("assess_check_openstack") |
36 | + |
37 | +JUJU_IMAGE_INDEX = "images/streams/v1/index.json" |
38 | +JUJU_TOOL_INDEX = "tools/streams/v1/index2.json" |
39 | + |
40 | + |
41 | +def do_validate_image(image, region, endpoint, series, source_dir, client): |
42 | + """ |
43 | + Invoke cmd 'juju metadata validate-images' to validate |
44 | + image metadata and ensure image(s) exist for a model |
45 | + :param image: String representing image ID |
46 | + :param region: String representing region name |
47 | + :param endpoint: String representing endpoint |
48 | + :param series: String representing series |
49 | + :param source_dir: The source directory containing tools and metadata |
50 | + :param client: Juju client |
51 | + """ |
52 | + try: |
53 | + output = client.validate_images( |
54 | + source_dir, region, endpoint, series, provider="openstack") |
55 | + except subprocess.CalledProcessError as e: |
56 | + raise JujuAssertionError( |
57 | + "Failed to execute juju metadata validate-images: {}".format(e)) |
58 | + |
59 | + validate_images_output = json.loads(output) |
60 | + actual_image_id = validate_images_output["ImageIds"][0] |
61 | + actual_region_name = validate_images_output["Region"] |
62 | + actual_index_url = validate_images_output["Resolve Metadata"]["indexURL"] |
63 | + |
64 | + if actual_image_id != image: |
65 | + raise JujuAssertionError( |
66 | + 'juju validate-image failed for image. Expected {} Got {}'.format( |
67 | + image, actual_image_id)) |
68 | + |
69 | + if actual_region_name != region: |
70 | + raise JujuAssertionError( |
71 | + 'juju validate-image failed for region. Expected {} Got {}'.format( |
72 | + region, actual_region_name)) |
73 | + |
74 | + index_url = os.path.join("file:///", source_dir[1:], JUJU_IMAGE_INDEX) |
75 | + if actual_index_url != index_url: |
76 | + raise JujuAssertionError( |
77 | + 'juju validate-image failed for indexURL. ' |
78 | + 'Expected {} Got {}'.format(index_url, actual_index_url)) |
79 | + |
80 | + log.info("juju validate-image done successfully") |
81 | + |
82 | + |
83 | +def do_validate_tool(region, endpoint, series, stream, source_dir, client): |
84 | + """ |
85 | + Invoke juju cmd 'juju metadata validate-tools' to validate tools |
86 | + metadata and ensure tool tarball exist for Juju version |
87 | + :param region: String representing region name |
88 | + :param endpoint: String representing endpoint |
89 | + :param series: String representing series |
90 | + :param stream: String representing stream |
91 | + :param source_dir: The source directory containing tools and metadata |
92 | + :param client: Juju client |
93 | + """ |
94 | + try: |
95 | + output = client.validate_tools( |
96 | + source_dir, region, endpoint, series, stream, provider="openstack") |
97 | + validate_tool_output = json.loads(output) |
98 | + actual_index_url = validate_tool_output["Resolve Metadata"]["indexURL"] |
99 | + |
100 | + index_url = os.path.join("file:///", source_dir[1:], JUJU_TOOL_INDEX) |
101 | + if actual_index_url != index_url: |
102 | + raise JujuAssertionError( |
103 | + 'juju validate-tools failed for indexURL. ' |
104 | + 'Expcted {} Got {}'.format(index_url, actual_index_url)) |
105 | + |
106 | + except subprocess.CalledProcessError as e: |
107 | + raise JujuAssertionError( |
108 | + "Failed to execute juju metadata validate-tools {}".format(e)) |
109 | + |
110 | + log.info("juju validate-tools done successfully") |
111 | + |
112 | + |
113 | +def copy_agent_file(stream, src_agent_file, agent_dir): |
114 | + """ |
115 | + Copy the specified source agent file to the specified |
116 | + target agent directory. |
117 | + :param stream: The released or develop stream directory to be created |
118 | + :param src_agent_file: The source juju agent file |
119 | + :param agent_dir: The directory to copy the orignal juju agent file |
120 | + """ |
121 | + if stream.startswith("releas"): |
122 | + target_dir = os.path.join(agent_dir, "tools", "released") |
123 | + else: |
124 | + target_dir = os.path.join(agent_dir, "tools", "develop") |
125 | + |
126 | + os.makedirs(target_dir) |
127 | + |
128 | + dst_agent_file = os.path.join(target_dir, os.path.basename(src_agent_file)) |
129 | + copyfile(src_agent_file, dst_agent_file) |
130 | + |
131 | + |
132 | +def assess_check_openstack(args): |
133 | + """ |
134 | + Perform juju validataion for image and tool metadata |
135 | + :param args: Parsed command line arguments |
136 | + """ |
137 | + bs_manager = BootstrapManager.from_args(args) |
138 | + client = bs_manager.client |
139 | + with temp_dir() as agent_dir: |
140 | + copy_agent_file(args.stream, args.agent_file, agent_dir) |
141 | + client.generate_image(agent_dir, args.image, args.arch, args.series, |
142 | + args.region, args.endpoint) |
143 | + client.generate_tool(agent_dir, args.stream) |
144 | + do_validate_image(args.image, args.region, args.endpoint, args.series, |
145 | + agent_dir, client) |
146 | + do_validate_tool(args.region, args.endpoint, args.series, args.stream, |
147 | + agent_dir, client) |
148 | + |
149 | + |
150 | +def parse_args(argv): |
151 | + """Parse all arguments.""" |
152 | + parser = argparse.ArgumentParser( |
153 | + description="Perform juju metadata validation for images and tools") |
154 | + add_basic_testing_arguments(parser) |
155 | + |
156 | + parser.add_argument('--arch', |
157 | + default='amd64', |
158 | + help='the image architecture') |
159 | + parser.add_argument('--stream', |
160 | + default='released', |
161 | + help='the image stream', |
162 | + choices=["released", "develop"]) |
163 | + parser.add_argument('--endpoint', required=True, |
164 | + default=None, |
165 | + help='the regional cloud endpoint') |
166 | + parser.add_argument('--image', required=True, |
167 | + default=None, |
168 | + help='the image id') |
169 | + parser.add_argument('--agent-file', required=True, |
170 | + default=None, |
171 | + help='agent file to be used during bootstrap.') |
172 | + |
173 | + return parser.parse_args(argv) |
174 | + |
175 | + |
176 | +def main(argv=None): |
177 | + args = parse_args(argv) |
178 | + configure_logging(args.verbose) |
179 | + |
180 | + if args.series is None: |
181 | + raise JujuAssertionError("argument --series is required") |
182 | + |
183 | + if args.region is None: |
184 | + raise JujuAssertionError("argument --region is required") |
185 | + |
186 | + assess_check_openstack(args) |
187 | + |
188 | + return 0 |
189 | + |
190 | +if __name__ == '__main__': |
191 | + sys.exit(main()) |
192 | |
193 | === modified file 'jujupy.py' |
194 | --- jujupy.py 2017-01-03 17:04:07 +0000 |
195 | +++ jujupy.py 2017-01-06 14:05:59 +0000 |
196 | @@ -2725,6 +2725,32 @@ |
197 | return self.get_juju_output('list-clouds', '--format', |
198 | format, include_e=False) |
199 | |
200 | + def validate_tools(self, source_dir, region, endpoint, series, stream, |
201 | + provider, format='json'): |
202 | + args = ('validate-tools', '-p', provider, '-s', series, '-r', region, |
203 | + '-d', source_dir, '-u', endpoint, '--stream', stream) |
204 | + args += ('--format', format) |
205 | + return self.get_juju_output("metadata", *args, include_e=False) |
206 | + |
207 | + def validate_images(self, source_dir, region, endpoint, series, provider, |
208 | + format='json'): |
209 | + args = ('validate-images', '-p', provider, '-s', series, '-r', region, |
210 | + '-d', source_dir, '-u', endpoint) |
211 | + args += ('--format', format) |
212 | + return self.get_juju_output("metadata", *args, include_e=False) |
213 | + |
214 | + def generate_image(self, source_dir, image, arch, series, region, |
215 | + endpoint): |
216 | + args = ('generate-image', '-a', arch, '-i', image, '-r', region, |
217 | + '-s', series, '-u', endpoint, '-d', source_dir) |
218 | + return self.juju("metadata", args, include_e=False) |
219 | + |
220 | + def generate_tool(self, source_dir, stream=None): |
221 | + args = ('generate-tools', '-d', source_dir) |
222 | + if stream is not None: |
223 | + args += ('--stream', stream) |
224 | + return self.juju('metadata', args, include_e=False) |
225 | + |
226 | def add_cloud_interactive(self, cloud_name, cloud): |
227 | child = self.expect('add-cloud', include_e=False) |
228 | try: |
229 | |
230 | === added file 'tests/test_assess_check_openstack.py' |
231 | --- tests/test_assess_check_openstack.py 1970-01-01 00:00:00 +0000 |
232 | +++ tests/test_assess_check_openstack.py 2017-01-06 14:05:59 +0000 |
233 | @@ -0,0 +1,108 @@ |
234 | + |
235 | +from tests import ( |
236 | + TestCase, |
237 | + ) |
238 | + |
239 | +from mock import ( |
240 | + patch, |
241 | + ) |
242 | +from fakejuju import ( |
243 | + fake_juju_client, |
244 | + ) |
245 | +from utility import ( |
246 | + JujuAssertionError, |
247 | + ) |
248 | + |
249 | +from assess_check_openstack import ( |
250 | + do_validate_image, |
251 | + do_validate_tool, |
252 | + parse_args, |
253 | + ) |
254 | + |
255 | + |
256 | +class TestParseArgs(TestCase): |
257 | + def test_parse_args_(self): |
258 | + args = parse_args(['base', 'foo', 'bar', |
259 | + '--series', 'amd64', |
260 | + '--agent-file', |
261 | + '/home/juju-2.1-beta1-xenial-amd64.tgz', |
262 | + '--endpoint', 'http://example.juju.com', |
263 | + '--image', '843bef24-8c70-40e1-a380-972717c9e4b3', |
264 | + '--region', 'foo']) |
265 | + self.assertEqual(args.endpoint, "http://example.juju.com") |
266 | + self.assertEqual(args.image, "843bef24-8c70-40e1-a380-972717c9e4b3") |
267 | + self.assertEqual(args.region, "foo") |
268 | + self.assertEqual(args.agent_file, |
269 | + "/home/juju-2.1-beta1-xenial-amd64.tgz") |
270 | + |
271 | + |
272 | +class TestAssessCheckOpenStack(TestCase): |
273 | + def test_do_validate_image(self): |
274 | + args = parse_args(['base', 'foo', 'bar', |
275 | + '--series', 'amd64', |
276 | + '--agent-file', |
277 | + '/home/juju-2.1-beta1-xenial-amd64.tgz', |
278 | + '--endpoint', 'http://example.juju.com', |
279 | + '--image', '843bef24-8c70-40e1-a380-972717c9e4b3', |
280 | + '--region', 'foo']) |
281 | + mock_client = fake_juju_client() |
282 | + output = '{' \ |
283 | + '"ImageIds":["843bef24-8c70-40e1-a380-972717c9e4b3"],' \ |
284 | + '"Region":"foo","Resolve Metadata":' \ |
285 | + '{' \ |
286 | + '"source":"local metadata directory",' \ |
287 | + '"signed":false,' \ |
288 | + '"indexURL":' \ |
289 | + '"file:///tmp/tmpFpwJ3k/images/streams/v1/index.json"' \ |
290 | + '}' \ |
291 | + '}\n' |
292 | + |
293 | + with patch('jujupy.EnvJujuClient.validate_images', |
294 | + return_value=output, autospec=True): |
295 | + do_validate_image(args.image, args.region, args.endpoint, |
296 | + args.series, "/tmp/tmpFpwJ3k", mock_client) |
297 | + |
298 | + def test_do_validate_image_with_assert_raises(self): |
299 | + args = parse_args(['base', 'foo', 'bar', |
300 | + '--series', 'amd64', |
301 | + '--agent-file', |
302 | + '/home/juju-2.1-beta1-xenial-amd64.tgz', |
303 | + '--endpoint', 'http://example.juju.com', |
304 | + '--image', '843bef24-8c70-40e1-a380-972717c9e4b3', |
305 | + '--region', 'foo']) |
306 | + mock_client = fake_juju_client() |
307 | + output = '{' \ |
308 | + '"ImageIds":["843bef24-8c70-40e1-a380-972717c9e4"],' \ |
309 | + '"Region":"foo","Resolve Metadata":' \ |
310 | + '{' \ |
311 | + '"source":"local metadata directory",' \ |
312 | + '"signed":false,' \ |
313 | + '"indexURL":' \ |
314 | + '"file:///tmp/tmpFpwJ3k/images/streams/v1/index.json"' \ |
315 | + '}' \ |
316 | + '}\n' |
317 | + |
318 | + with patch('jujupy.EnvJujuClient.validate_images', |
319 | + return_value=output, autospec=True): |
320 | + with self.assertRaises(JujuAssertionError): |
321 | + do_validate_image(args.image, args.region, args.endpoint, |
322 | + args.series, "/tmp/tmpFpwJ3k", mock_client) |
323 | + |
324 | + def test_do_validate_tools(self): |
325 | + args = parse_args(['base', 'foo', 'bar', |
326 | + '--agent-file', |
327 | + '/home/juju-2.1-beta1-xenial-amd64.tgz', |
328 | + '--endpoint', 'http://example.juju.com', |
329 | + '--image', '843bef24-8c70-40e1-a380-972717c9e4b3', |
330 | + '--region', 'foo']) |
331 | + mock_client = fake_juju_client() |
332 | + output = '{"Matching Tools Versions":["2.0.1-xenial-amd64"],' \ |
333 | + '"Resolve Metadata":{' \ |
334 | + '"source":"local source",' \ |
335 | + '"signed":false,' \ |
336 | + '"indexURL":' \ |
337 | + '"file:///tmp/tmpRo0uM_/tools/streams/v1/index2.json"}}\n' |
338 | + with patch('jujupy.EnvJujuClient.validate_tools', |
339 | + return_value=output, autospec=True): |
340 | + do_validate_tool(args.region, args.endpoint, args.series, |
341 | + args.stream, "/tmp/tmpRo0uM_", mock_client) |
342 | |
343 | === modified file 'tests/test_jujupy.py' |
344 | --- tests/test_jujupy.py 2017-01-03 17:04:07 +0000 |
345 | +++ tests/test_jujupy.py 2017-01-06 14:05:59 +0000 |
346 | @@ -3479,6 +3479,56 @@ |
347 | mock.assert_called_with( |
348 | 'list-clouds', '--format', 'json', include_e=False) |
349 | |
350 | + def test_validate_tools(self): |
351 | + client = EnvJujuClient(JujuData('foo'), None, None) |
352 | + with patch.object(client, 'get_juju_output', autospec=True, |
353 | + return_value='') as mock: |
354 | + client.validate_tools('source_dir', 'region', 'endpoint', |
355 | + 'series', 'stream', 'provider') |
356 | + mock.assert_called_once_with('metadata', 'validate-tools', |
357 | + '-p', 'provider', '-s', 'series', |
358 | + '-r', 'region', '-d', 'source_dir', |
359 | + '-u', 'endpoint', '--stream', 'stream', |
360 | + '--format', 'json', |
361 | + include_e=False) |
362 | + |
363 | + def test_validate_images(self): |
364 | + client = EnvJujuClient(JujuData('foo'), None, None) |
365 | + with patch.object(client, 'get_juju_output', autospec=True, |
366 | + return_value='') as mock: |
367 | + client.validate_images('source_dir', 'region', 'endpoint', |
368 | + 'series', 'provider') |
369 | + mock.assert_called_once_with('metadata', 'validate-images', |
370 | + '-p', 'provider', '-s', 'series', |
371 | + '-r', 'region', '-d', 'source_dir', |
372 | + '-u', 'endpoint', '--format', 'json', |
373 | + include_e=False) |
374 | + |
375 | + def test_generate_image(self): |
376 | + client = EnvJujuClient(JujuData('foo'), None, None) |
377 | + args = ('generate-image', '-a', 'arch', '-i', 'image', '-r', |
378 | + 'region', '-s', 'series', '-u', 'endpoint', '-d', 'source_dir') |
379 | + with patch.object(client, 'juju', autospec=True) as mock: |
380 | + client.generate_image('source_dir', 'image', 'arch', 'series', |
381 | + 'region', 'endpoint') |
382 | + mock.assert_called_once_with('metadata', args, include_e=False) |
383 | + |
384 | + def test_generate_tool(self): |
385 | + client = EnvJujuClient(JujuData('foo'), None, None) |
386 | + with patch.object(client, 'juju', autospec=True) as mock: |
387 | + client.generate_tool('/agents') |
388 | + mock.assert_called_once_with('metadata', |
389 | + ('generate-tools', '-d', '/agents'), |
390 | + include_e=False) |
391 | + |
392 | + def test_generate_tool_with_stream(self): |
393 | + client = EnvJujuClient(JujuData('foo'), None, None) |
394 | + with patch.object(client, 'juju', autospec=True) as mock: |
395 | + client.generate_tool('/agents', "testing") |
396 | + mock.assert_called_once_with( |
397 | + 'metadata', ('generate-tools', '-d', '/agents', |
398 | + '--stream', 'testing'), include_e=False) |
399 | + |
400 | def test_add_cloud_interactive_maas(self): |
401 | client = fake_juju_client() |
402 | clouds = {'foo': { |
A couple of comments inline.