Merge lp:~viswesn/juju-ci-tools/assess-check-openstack into lp:juju-ci-tools

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
Reviewer Review Type Date Requested Status
Christopher Lee (community) Needs Fixing
Review via email: mp+313036@code.launchpad.net

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.
Revision history for this message
Christopher Lee (veebers) wrote :

A couple of comments inline.

review: Needs Fixing
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': {

Subscribers

People subscribed via source and target branches