Merge lp:~bac/juju-quickstart/platform-settings-2 into lp:juju-quickstart

Proposed by Brad Crittenden
Status: Merged
Merged at revision: 71
Proposed branch: lp:~bac/juju-quickstart/platform-settings-2
Merge into: lp:juju-quickstart
Diff against target: 963 lines (+230/-176)
9 files modified
quickstart/app.py (+11/-15)
quickstart/manage.py (+41/-14)
quickstart/platform_support.py (+31/-38)
quickstart/settings.py (+16/-2)
quickstart/tests/test_app.py (+46/-51)
quickstart/tests/test_manage.py (+57/-16)
quickstart/tests/test_platform_support.py (+23/-35)
quickstart/tests/test_utils.py (+3/-3)
quickstart/utils.py (+2/-2)
To merge this branch: bzr merge lp:~bac/juju-quickstart/platform-settings-2
Reviewer Review Type Date Requested Status
Juju GUI Hackers Pending
Review via email: mp+221745@code.launchpad.net

Description of the change

Reorganize platform settings.

The first cut of the platform work was a bit unclean with respect to the
dividing lines between the quickstart app code and the parts that can be
re-used as a library. This branch moves things around to re-attain that
separation.

https://codereview.appspot.com/101980050/

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

Reviewers: mp+221745_code.launchpad.net,

Message:
Please take a look.

Description:
Reorganize platform settings.

The first cut of the platform work was a bit unclean with respect to the
dividing lines between the quickstart app code and the parts that can be
re-used as a library. This branch moves things around to re-attain that
separation.

Francesco's original concerns are raised in the previous code review at:

https://codereview.appspot.com/102870043

https://code.launchpad.net/~bac/juju-quickstart/platform-settings-2/+merge/221745

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/101980050/

Affected files (+232, -176 lines):
   A [revision details]
   M quickstart/app.py
   M quickstart/manage.py
   M quickstart/platform_support.py
   M quickstart/settings.py
   M quickstart/tests/test_app.py
   M quickstart/tests/test_manage.py
   M quickstart/tests/test_platform_support.py
   M quickstart/tests/test_utils.py
   M quickstart/utils.py

Revision history for this message
Brad Crittenden (bac) wrote :

*** Submitted:

Reorganize platform settings.

The first cut of the platform work was a bit unclean with respect to the
dividing lines between the quickstart app code and the parts that can be
re-used as a library. This branch moves things around to re-attain that
separation.

R=redir
CC=
https://codereview.appspot.com/101980050

https://codereview.appspot.com/101980050/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'quickstart/app.py'
2--- quickstart/app.py 2014-05-29 18:07:12 +0000
3+++ quickstart/app.py 2014-06-02 14:34:25 +0000
4@@ -58,7 +58,7 @@
5 return b'juju-quickstart: error: {}'.format(self.message)
6
7
8-def ensure_dependencies(distro_only):
9+def ensure_dependencies(distro_only, platform, juju_command):
10 """Ensure that Juju and LXC are installed.
11
12 If the "juju" command is not found in the PATH, then install and
13@@ -77,22 +77,19 @@
14 the Juju version.
15 """
16 try:
17- platform, installer = platform_support.get_platform_installer()
18+ installer = platform_support.get_juju_installer(platform)
19 except platform_support.UnsupportedOS as err:
20 raise ProgramExit(bytes(err))
21
22- if installer is None:
23- raise ProgramExit(b'No installer found for host platform.')
24-
25 required_packages = []
26 # Check if juju is installed.
27 try:
28- juju_version = utils.get_juju_version()
29+ juju_version = utils.get_juju_version(juju_command)
30 except ValueError:
31 # Juju is not installed or configured properly. To ensure everything
32 # is set up correctly, also install packages required to run
33 # environments using the local provider.
34- required_packages.extend(platform_support.JUJU_PACKAGES.get(platform))
35+ required_packages.extend(settings.JUJU_PACKAGES.get(platform))
36 juju_version = None
37 else:
38 if platform_support.supports_local(platform):
39@@ -113,7 +110,7 @@
40 if juju_version is None:
41 # Juju has been just installed, retrieve its version.
42 try:
43- juju_version = utils.get_juju_version()
44+ juju_version = utils.get_juju_version(juju_command)
45 except ValueError as err:
46 raise ProgramExit(bytes(err))
47 return juju_version
48@@ -161,13 +158,12 @@
49 b'\nIf you would like to create the keys yourself,\n'
50 b'please run this command, follow its instructions,\n'
51 b'and then re-run quickstart:\n'
52- b' ssh-keygen -b 4096 -t rsa'
53- )
54+ b' ssh-keygen -b 4096 -t rsa')
55 except OSError as err:
56 raise ProgramExit(bytes(err))
57
58
59-def bootstrap(env_name, requires_sudo=False, debug=False):
60+def bootstrap(env_name, juju_command, requires_sudo=False, debug=False):
61 """Bootstrap the Juju environment with the given name.
62
63 Do not bootstrap the environment if already bootstrapped.
64@@ -187,7 +183,7 @@
65 Raise a ProgramExit if any error occurs in the bootstrap process.
66 """
67 already_bootstrapped = False
68- cmd = [settings.JUJU_CMD, 'bootstrap', '-e', env_name]
69+ cmd = [juju_command, 'bootstrap', '-e', env_name]
70 if requires_sudo:
71 cmd.insert(0, 'sudo')
72 if debug:
73@@ -224,7 +220,7 @@
74 timeout = time.time() + (60*10)
75 while time.time() < timeout:
76 retcode, output, error = utils.call(
77- settings.JUJU_CMD, 'status', '-e', env_name, '--format', 'yaml')
78+ juju_command, 'status', '-e', env_name, '--format', 'yaml')
79 if retcode:
80 continue
81 # Ensure the state server is up and the agent is started.
82@@ -266,7 +262,7 @@
83 raise ValueError(msg.encode('utf-8'))
84
85
86-def get_api_url(env_name):
87+def get_api_url(env_name, juju_command):
88 """Return a Juju API URL for the given environment name.
89
90 Use the Juju CLI in a subprocess in order to retrieve the API addresses.
91@@ -274,7 +270,7 @@
92 Raise a ProgramExit if any error occurs.
93 """
94 retcode, output, error = utils.call(
95- settings.JUJU_CMD, 'api-endpoints', '-e', env_name, '--format', 'json')
96+ juju_command, 'api-endpoints', '-e', env_name, '--format', 'json')
97 if retcode:
98 raise ProgramExit(error)
99 # Assuming there is always at least one API address, grab the first one
100
101=== modified file 'quickstart/manage.py'
102--- quickstart/manage.py 2014-05-30 12:26:45 +0000
103+++ quickstart/manage.py 2014-06-02 14:34:25 +0000
104@@ -33,6 +33,7 @@
105 from quickstart import (
106 app,
107 packaging,
108+ platform_support,
109 settings,
110 utils,
111 )
112@@ -54,7 +55,7 @@
113 parser.exit()
114
115
116-def _get_packaging_info(juju_source):
117+def _get_packaging_info(juju_source, platform):
118 """Return packaging info based on the given juju source.
119
120 The juju_source argument can be either "ppa" or "distro".
121@@ -65,16 +66,23 @@
122 - distro_only_help: the help text for the --distro-only flag;
123 - ppa_help: the help text for the --ppa flag.
124 """
125- distro_only_help = ('Do not use external sources when installing and '
126- 'setting up Juju')
127- ppa_help = 'Use external sources when installing and setting up Juju'
128- disable_help = '\n(enabled by default, use {} to disable)'
129- if juju_source == 'distro':
130+ if platform == settings.LINUX_APT:
131+ distro_only_help = ('Do not use external sources when installing and '
132+ 'setting up Juju')
133+ ppa_help = 'Use external sources when installing and setting up Juju'
134+ disable_help = '\n(enabled by default, use {} to disable)'
135+ if juju_source == 'distro':
136+ distro_only = True
137+ distro_only_help += disable_help.format('--ppa')
138+ else:
139+ distro_only = False
140+ ppa_help += disable_help.format('--distro-only')
141+ else:
142+ # If not LINUX_APT then distro_only and ppa make no sense. Prevent
143+ # the user from seeing them but allow reasonable values in options.
144 distro_only = True
145- distro_only_help += disable_help.format('--ppa')
146- else:
147- distro_only = False
148- ppa_help += disable_help.format('--distro-only')
149+ distro_only_help = argparse.SUPPRESS
150+ ppa_help = argparse.SUPPRESS
151 return distro_only, distro_only_help, ppa_help
152
153
154@@ -329,6 +337,7 @@
155 - env_type: the provider type of the selected Juju environment;
156 - interactive: whether to start the interactive session;
157 - open_browser: whether the GUI browser must be opened.
158+ - platform: The host platform.
159
160 The following attributes will also be included in the namespace if a bundle
161 deployment is requested:
162@@ -340,9 +349,17 @@
163
164 Exit with an error if the provided arguments are not valid.
165 """
166+
167+ # Determine the host platform. This needs to be done early as it affects
168+ # the options we present and allows an early failure.
169+ try:
170+ platform = platform_support.get_platform()
171+ except platform_support.UnsupportedOS as err:
172+ raise app.ProgramExit(bytes(err))
173+
174 default_env_name = envs.get_default_env_name()
175 default_distro_only, distro_only_help, ppa_help = _get_packaging_info(
176- packaging.JUJU_SOURCE)
177+ packaging.JUJU_SOURCE, platform)
178 # Define the help message for the --environment option.
179 env_help = 'The name of the Juju environment to use'
180 if default_env_name is not None:
181@@ -401,6 +418,7 @@
182 parser.add_argument(
183 '--no-browser', action='store_false', dest='open_browser',
184 help='Avoid opening the browser to the GUI at the end of the\nprocess')
185+
186 parser.add_argument(
187 '--distro-only', action='store_true', dest='distro_only',
188 default=default_distro_only, help=distro_only_help)
189@@ -420,6 +438,10 @@
190 nargs=0, help="Show program's description and exit")
191 # Parse the provided arguments.
192 options = parser.parse_args()
193+
194+ # Add in the platform for convenience.
195+ options.platform = platform
196+
197 # Convert the provided string arguments to unicode.
198 _convert_options_to_unicode(options)
199 # Validate and process the provided arguments.
200@@ -440,8 +462,11 @@
201 print('contents loaded for bundle {} (services: {})'.format(
202 options.bundle_name, len(options.bundle_services)))
203
204+ juju_command = platform_support.get_juju_command(options.platform)
205+
206 logging.debug('ensuring juju and dependencies are installed')
207- juju_version = app.ensure_dependencies(options.distro_only)
208+ juju_version = app.ensure_dependencies(
209+ options.distro_only, options.platform, juju_command)
210
211 logging.debug('ensuring SSH keys are available')
212 app.ensure_ssh_keys()
213@@ -456,8 +481,10 @@
214 print('sudo privileges will be required to bootstrap the environment')
215 # If the Juju version is less than 1.17.2 then use sudo for local envs.
216 requires_sudo = juju_version < (1, 17, 2)
217+
218 already_bootstrapped, bootstrap_node_series = app.bootstrap(
219- options.env_name, requires_sudo=requires_sudo, debug=options.debug)
220+ options.env_name, juju_command,
221+ requires_sudo=requires_sudo, debug=options.debug)
222
223 # Retrieve the admin-secret for the current environment.
224 try:
225@@ -472,7 +499,7 @@
226 raise app.ProgramExit(msg)
227
228 print('retrieving the Juju API address')
229- api_url = app.get_api_url(options.env_name)
230+ api_url = app.get_api_url(options.env_name, juju_command)
231 print('connecting to {}'.format(api_url))
232 env = app.connect(api_url, admin_secret)
233
234
235=== modified file 'quickstart/platform_support.py'
236--- quickstart/platform_support.py 2014-05-29 20:49:20 +0000
237+++ quickstart/platform_support.py 2014-06-02 14:34:25 +0000
238@@ -36,12 +36,6 @@
239 pass
240
241
242-OSX = object()
243-LINUX_APT = object()
244-LINUX_RPM = object()
245-WINDOWS = object()
246-
247-
248 def get_platform():
249 """Return the platform of the host.
250
251@@ -49,14 +43,14 @@
252 """
253 system = platform.system()
254 if system == 'Darwin':
255- return OSX
256+ return settings.OSX
257 elif system == 'Windows':
258- return WINDOWS
259+ return settings.WINDOWS
260 elif system == 'Linux':
261 if os.path.isfile('/usr/bin/apt-get'):
262- return LINUX_APT
263+ return settings.LINUX_APT
264 elif os.path.isfile('/usr/bin/rpm'):
265- return LINUX_RPM
266+ return settings.LINUX_RPM
267 raise UnsupportedOS(b'{} without apt-get nor rpm'.format(system))
268 elif system == '':
269 raise UnsupportedOS(b'Unable to determine the OS platform')
270@@ -97,35 +91,34 @@
271
272
273 INSTALLERS = {
274- LINUX_APT: _installer_apt,
275- OSX: _installer_osx,
276-}
277-
278-
279-JUJU_PACKAGES = {
280- LINUX_APT: ('juju-core', 'juju-local'),
281- OSX: ('juju', ),
282-}
283-
284-
285-ALTERNATE_JUJU_LOCATIONS = {
286- OSX: '/usr/local/bin/juju',
287-}
288-
289-
290-def get_platform_installer():
291- """Return the platorm and the installer for the host.
292-
293- Raises UnsupportedOS if the OS is unknown.
294- Returns platform and installer callable. The installer_callable is None
295- if no installer can be found for the platform.
296- """
297- platform = get_platform()
298- if platform in ALTERNATE_JUJU_LOCATIONS:
299- settings.JUJU_CMD = ALTERNATE_JUJU_LOCATIONS[platform]
300- return platform, INSTALLERS.get(platform)
301+ settings.LINUX_APT: _installer_apt,
302+ settings.OSX: _installer_osx,
303+}
304+
305+
306+def get_juju_command(platform):
307+ """Return the path to the Juju command on the given platform.
308+
309+ If the platform does not have a novel location, the default will be
310+ returned.
311+ """
312+ return settings.JUJU_CMD_PATHS.get(
313+ platform,
314+ settings.JUJU_CMD_PATHS['default'])
315+
316+
317+def get_juju_installer(platform):
318+ """Return the installer for the host platform.
319+
320+ Returns installer callable.
321+ Raises UnsupportedOS if a platform we don't support is detected.
322+ """
323+ installer = INSTALLERS.get(platform)
324+ if installer is None:
325+ raise UnsupportedOS(b'No installer found for host platform.')
326+ return installer
327
328
329 def supports_local(platform):
330 """Return True if the platform supports local (LXC) deploys."""
331- return platform in (LINUX_APT, LINUX_RPM)
332+ return platform in (settings.LINUX_APT, settings.LINUX_RPM)
333
334=== modified file 'quickstart/settings.py'
335--- quickstart/settings.py 2014-04-23 15:53:32 +0000
336+++ quickstart/settings.py 2014-06-02 14:34:25 +0000
337@@ -21,6 +21,11 @@
338 import collections
339 import os
340
341+# Platforms.
342+OSX = object()
343+LINUX_APT = object()
344+LINUX_RPM = object()
345+WINDOWS = object()
346
347 # The base charmworld API URL containing information about charms.
348 # This URL must be formatted with a series and a charm name.
349@@ -41,8 +46,17 @@
350 # The URL namespace for bundles in jujucharms.com.
351 JUJUCHARMS_BUNDLE_URL = 'https://jujucharms.com/bundle/'
352
353-# The path to the Juju command.
354-JUJU_CMD = '/usr/bin/juju'
355+# The path to the Juju command, based on platform.
356+JUJU_CMD_PATHS = {
357+ 'default': '/usr/bin/juju',
358+ OSX: '/usr/local/bin/juju',
359+}
360+
361+# Juju packages to install per platform.
362+JUJU_PACKAGES = {
363+ LINUX_APT: ('juju-core', 'juju-local'),
364+ OSX: ('juju', ),
365+}
366
367 # The possible values for the environments.yaml default-series field.
368 JUJU_DEFAULT_SERIES = ('precise', 'quantal', 'raring', 'saucy', 'trusty')
369
370=== modified file 'quickstart/tests/test_app.py'
371--- quickstart/tests/test_app.py 2014-05-29 18:07:12 +0000
372+++ quickstart/tests/test_app.py 2014-06-02 14:34:25 +0000
373@@ -68,10 +68,11 @@
374 add_repository = '/usr/bin/add-apt-repository'
375 apt_get = '/usr/bin/apt-get'
376 brew = '/usr/local/bin/brew'
377+ juju_command = settings.JUJU_CMD_PATHS['default']
378
379 def call_ensure_dependencies(
380 self, call_effects, distro_only=False,
381- platform=platform_support.LINUX_APT,
382+ platform=settings.LINUX_APT,
383 platform_installer=platform_support._installer_apt):
384 """Execute the quickstart.app.ensure_dependencies call.
385
386@@ -81,10 +82,10 @@
387 Return the mock call object and the ensure_dependencies return value.
388 """
389 with self.patch_multiple_calls(call_effects) as mock_call:
390- with mock.patch(
391- 'quickstart.app.platform_support.get_platform_installer',
392- side_effect=[(platform, platform_installer)]):
393- juju_version = app.ensure_dependencies(distro_only)
394+ path = 'quickstart.app.platform_support.get_juju_installer'
395+ with mock.patch(path, side_effect=[platform_installer]):
396+ juju_version = app.ensure_dependencies(
397+ distro_only, platform, self.juju_command)
398 return mock_call, juju_version
399
400 def test_success_install_apt(self, mock_print):
401@@ -101,7 +102,7 @@
402 mock_call, juju_version = self.call_ensure_dependencies(side_effects)
403 self.assertEqual(len(side_effects), mock_call.call_count)
404 mock_call.assert_has_calls([
405- mock.call(settings.JUJU_CMD, 'version'),
406+ mock.call(self.juju_command, 'version'),
407 mock.call('lsb_release', '-cs'),
408 mock.call('sudo', self.apt_get, 'install', '-y',
409 'software-properties-common'),
410@@ -109,7 +110,7 @@
411 mock.call('sudo', self.apt_get, 'update'),
412 mock.call('sudo', self.apt_get, 'install', '-y',
413 'juju-core', 'juju-local'),
414- mock.call(settings.JUJU_CMD, 'version'),
415+ mock.call(self.juju_command, 'version'),
416 ])
417 mock_print.assert_has_calls([
418 mock.call('adding the ppa:juju/stable PPA repository'),
419@@ -128,37 +129,27 @@
420 (0, '1.18.0', ''), # Retrieve the version again.
421 )
422 mock_call, juju_version = self.call_ensure_dependencies(
423- side_effects, platform=platform_support.OSX,
424+ side_effects, platform=settings.OSX,
425 platform_installer=platform_support._installer_osx)
426 self.assertEqual(len(side_effects), mock_call.call_count)
427 mock_call.assert_has_calls([
428- mock.call(settings.JUJU_CMD, 'version'),
429+ mock.call(self.juju_command, 'version'),
430 mock.call(self.brew, 'install', 'juju'),
431- mock.call(settings.JUJU_CMD, 'version'),
432+ mock.call(self.juju_command, 'version'),
433 ])
434 mock_print.assert_has_calls([
435 mock.call('Installing the following packages: juju\n'),
436 ])
437 self.assertEqual((1, 18, 0), juju_version)
438
439- def test_unsupported_os(self, mock_print):
440- # get_platform_installer raises UnsupportedOS, causing ProgramExit.
441- path = 'quickstart.app.platform_support.get_platform_installer'
442- err = platform_support.UnsupportedOS('Unknown TRS-80')
443- with mock.patch(path, side_effect=err):
444- with self.assertRaises(app.ProgramExit) as ctx:
445- app.ensure_dependencies(False)
446- expected = b'juju-quickstart: error: Unknown TRS-80'
447- self.assertEqual(expected, bytes(ctx.exception))
448-
449 def test_no_installer(self, mock_print):
450 # If no installer is found a ProgramExit is raised.
451- path = 'quickstart.app.platform_support.get_platform_installer'
452- with mock.patch(path, side_effect=[('unknown', None)]):
453+ path = 'quickstart.app.platform_support.get_juju_installer'
454+ err = platform_support.UnsupportedOS('unknown')
455+ with mock.patch(path, side_effect=err):
456 with self.assertRaises(app.ProgramExit) as ctx:
457- app.ensure_dependencies(False)
458- expected = (b'juju-quickstart: error: '
459- 'No installer found for host platform.')
460+ app.ensure_dependencies(False, None, None)
461+ expected = (b'juju-quickstart: error: unknown')
462 self.assertEqual(expected, bytes(ctx.exception))
463
464 def test_distro_only_install(self, mock_print):
465@@ -172,10 +163,10 @@
466 side_effects, distro_only=True)
467 self.assertEqual(len(side_effects), mock_call.call_count)
468 mock_call.assert_has_calls([
469- mock.call(settings.JUJU_CMD, 'version'),
470+ mock.call(self.juju_command, 'version'),
471 mock.call('sudo', self.apt_get, 'install', '-y',
472 'juju-core', 'juju-local'),
473- mock.call(settings.JUJU_CMD, 'version'),
474+ mock.call(self.juju_command, 'version'),
475 ])
476 mock_print.assert_called_once_with(
477 'sudo privileges will be used for the installation of \n'
478@@ -211,7 +202,7 @@
479 mock_call, juju_version = self.call_ensure_dependencies(side_effects)
480 self.assertEqual(len(side_effects), mock_call.call_count)
481 mock_call.assert_has_calls([
482- mock.call(settings.JUJU_CMD, 'version'),
483+ mock.call(self.juju_command, 'version'),
484 mock.call('/usr/bin/lxc-ls'),
485 mock.call('lsb_release', '-cs'),
486 mock.call('sudo', self.apt_get, 'install', '-y',
487@@ -241,7 +232,7 @@
488 side_effects, distro_only=True)
489 self.assertEqual(len(side_effects), mock_call.call_count)
490 mock_call.assert_has_calls([
491- mock.call(settings.JUJU_CMD, 'version'),
492+ mock.call(self.juju_command, 'version'),
493 mock.call('/usr/bin/lxc-ls'),
494 mock.call('sudo', self.apt_get, 'install', '-y', 'juju-local'),
495 ])
496@@ -286,7 +277,7 @@
497 with self.assert_program_exit('install error'):
498 mock_call = self.call_ensure_dependencies(
499 side_effects,
500- platform=platform_support.OSX,
501+ platform=settings.OSX,
502 platform_installer=platform_support._installer_osx)[0]
503 self.assertEqual(2, mock_call.call_count)
504
505@@ -447,6 +438,7 @@
506
507 env_name = 'my-juju-env'
508 status_message = 'retrieving the environment status'
509+ juju_command = settings.JUJU_CMD_PATHS['default']
510
511 def make_status_output(self, agent_state, series='hoary'):
512 """Create and return a YAML status output."""
513@@ -458,7 +450,7 @@
514 def make_status_calls(self, number):
515 """Return a list containing the given number of status calls."""
516 call = mock.call(
517- settings.JUJU_CMD, 'status', '-e', self.env_name,
518+ self.juju_command, 'status', '-e', self.env_name,
519 '--format', 'yaml')
520 return [call for _ in range(number)]
521
522@@ -475,19 +467,20 @@
523 Receive the list of side effects the mock status call will return.
524 """
525 with self.patch_multiple_calls(side_effects) as mock_call:
526- app.bootstrap(self.env_name)
527+ app.bootstrap(self.env_name, self.juju_command)
528 mock_call.assert_has_calls([
529- mock.call(settings.JUJU_CMD, 'bootstrap', '-e', self.env_name),
530+ mock.call(self.juju_command, 'bootstrap', '-e', self.env_name),
531 ] + self.make_status_calls(5))
532
533 def test_success(self, mock_print):
534 # The environment is successfully bootstrapped.
535 with self.patch_multiple_calls(self.make_side_effects()) as mock_call:
536- already_bootstrapped, series = app.bootstrap(self.env_name)
537+ already_bootstrapped, series = app.bootstrap(
538+ self.env_name, self.juju_command)
539 self.assertFalse(already_bootstrapped)
540 self.assertEqual(series, 'hoary')
541 mock_call.assert_has_calls([
542- mock.call(settings.JUJU_CMD, 'bootstrap', '-e', self.env_name),
543+ mock.call(self.juju_command, 'bootstrap', '-e', self.env_name),
544 ] + self.make_status_calls(1))
545 mock_print.assert_called_once_with(self.status_message)
546
547@@ -495,12 +488,12 @@
548 # The environment is bootstrapped with sudo using the local provider.
549 with self.patch_multiple_calls(self.make_side_effects()) as mock_call:
550 already_bootstrapped, series = app.bootstrap(
551- self.env_name, requires_sudo=True)
552+ self.env_name, self.juju_command, requires_sudo=True)
553 self.assertFalse(already_bootstrapped)
554 self.assertEqual(series, 'hoary')
555 mock_call.assert_has_calls([
556 mock.call(
557- 'sudo', settings.JUJU_CMD, 'bootstrap', '-e', self.env_name),
558+ 'sudo', self.juju_command, 'bootstrap', '-e', self.env_name),
559 ] + self.make_status_calls(1))
560 mock_print.assert_called_once_with(self.status_message)
561
562@@ -508,12 +501,12 @@
563 # The environment is successfully bootstrapped in debug mode.
564 with self.patch_multiple_calls(self.make_side_effects()) as mock_call:
565 already_bootstrapped, series = app.bootstrap(
566- self.env_name, debug=True)
567+ self.env_name, self.juju_command, debug=True)
568 self.assertFalse(already_bootstrapped)
569 self.assertEqual(series, 'hoary')
570 mock_call.assert_has_calls([
571 mock.call(
572- settings.JUJU_CMD, 'bootstrap', '-e', self.env_name,
573+ self.juju_command, 'bootstrap', '-e', self.env_name,
574 '--debug'),
575 ] + self.make_status_calls(1))
576
577@@ -525,11 +518,12 @@
578 (0, self.make_status_output('started', 'precise'), ''),
579 ]
580 with self.patch_multiple_calls(side_effects) as mock_call:
581- already_bootstrapped, series = app.bootstrap(self.env_name)
582+ already_bootstrapped, series = app.bootstrap(
583+ self.env_name, self.juju_command)
584 self.assertTrue(already_bootstrapped)
585 self.assertEqual(series, 'precise')
586 mock_call.assert_has_calls([
587- mock.call(settings.JUJU_CMD, 'bootstrap', '-e', self.env_name),
588+ mock.call(self.juju_command, 'bootstrap', '-e', self.env_name),
589 ] + self.make_status_calls(1))
590 existing_message = 'reusing the already bootstrapped {} environment'
591 mock_print.assert_has_calls([
592@@ -541,9 +535,9 @@
593 # A ProgramExit is raised if an error occurs while bootstrapping.
594 with self.patch_call(retcode=1, error='bad wolf') as mock_call:
595 with self.assert_program_exit('bad wolf'):
596- app.bootstrap(self.env_name)
597+ app.bootstrap(self.env_name, self.juju_command)
598 mock_call.assert_called_once_with(
599- settings.JUJU_CMD, 'bootstrap', '-e', self.env_name),
600+ self.juju_command, 'bootstrap', '-e', self.env_name),
601
602 def test_status_retry_error(self, mock_print):
603 # Before raising a ProgramExit, the functions tries to call
604@@ -602,9 +596,9 @@
605 expected = 'state server failure:\n{}'.format(status_output)
606 with self.patch_multiple_calls(side_effects) as mock_call:
607 with self.assert_program_exit(expected):
608- app.bootstrap(self.env_name)
609+ app.bootstrap(self.env_name, self.juju_command)
610 mock_call.assert_has_calls([
611- mock.call(settings.JUJU_CMD, 'bootstrap', '-e', self.env_name),
612+ mock.call(self.juju_command, 'bootstrap', '-e', self.env_name),
613 ] + self.make_status_calls(1))
614
615 def test_status_failure(self, mock_print):
616@@ -628,9 +622,9 @@
617 # the third for the second status check, the fourth should fail.
618 with mock.patch('time.time', mock_time):
619 with self.assert_program_exit(expected):
620- app.bootstrap(self.env_name)
621+ app.bootstrap(self.env_name, self.juju_command)
622 mock_call.assert_has_calls([
623- mock.call(settings.JUJU_CMD, 'bootstrap', '-e', self.env_name),
624+ mock.call(self.juju_command, 'bootstrap', '-e', self.env_name),
625 ] + self.make_status_calls(2))
626
627
628@@ -660,24 +654,25 @@
629 helpers.CallTestsMixin, ProgramExitTestsMixin, unittest.TestCase):
630
631 env_name = 'ec2'
632+ juju_command = settings.JUJU_CMD_PATHS['default']
633
634 def test_success(self):
635 # The API URL is correctly returned.
636 api_addresses = json.dumps(['api.example.com:17070', 'not-today'])
637 with self.patch_call(retcode=0, output=api_addresses) as mock_call:
638- api_url = app.get_api_url(self.env_name)
639+ api_url = app.get_api_url(self.env_name, self.juju_command)
640 self.assertEqual('wss://api.example.com:17070', api_url)
641 mock_call.assert_called_once_with(
642- settings.JUJU_CMD, 'api-endpoints', '-e', self.env_name,
643+ self.juju_command, 'api-endpoints', '-e', self.env_name,
644 '--format', 'json')
645
646 def test_failure(self):
647 # A ProgramExit is raised if an error occurs retrieving the API URL.
648 with self.patch_call(retcode=1, error='bad wolf') as mock_call:
649 with self.assert_program_exit('bad wolf'):
650- app.get_api_url(self.env_name)
651+ app.get_api_url(self.env_name, self.juju_command)
652 mock_call.assert_called_once_with(
653- settings.JUJU_CMD, 'api-endpoints', '-e', self.env_name,
654+ self.juju_command, 'api-endpoints', '-e', self.env_name,
655 '--format', 'json')
656
657
658
659=== modified file 'quickstart/tests/test_manage.py'
660--- quickstart/tests/test_manage.py 2014-05-23 13:26:56 +0000
661+++ quickstart/tests/test_manage.py 2014-06-02 14:34:25 +0000
662@@ -38,7 +38,10 @@
663 from quickstart.cli import views
664 from quickstart.models import envs
665 from quickstart.tests import helpers
666-from quickstart import app
667+from quickstart import (
668+ app,
669+ platform_support,
670+)
671
672
673 class TestDescriptionAction(unittest.TestCase):
674@@ -63,24 +66,34 @@
675 distro_only_disable = '(enabled by default, use --ppa to disable)'
676 ppa_disable = '(enabled by default, use --distro-only to disable)'
677
678- def test_ppa_source(self):
679+ def test_ppa_source_apt(self):
680 # The returned distro_only flag is set to False and the help texts are
681 # formatted accordingly when the passed Juju source is "ppa".
682 distro_only, distro_only_help, ppa_help = manage._get_packaging_info(
683- 'ppa')
684+ 'ppa', platform=settings.LINUX_APT)
685 self.assertFalse(distro_only)
686 self.assertNotIn(self.distro_only_disable, distro_only_help)
687 self.assertIn(self.ppa_disable, ppa_help)
688
689- def test_distro_source(self):
690+ def test_distro_source_apt(self):
691 # The returned distro_only flag is set to True and the help texts are
692 # formatted accordingly when the passed Juju source is "distro".
693 distro_only, distro_only_help, ppa_help = manage._get_packaging_info(
694- 'distro')
695+ 'distro', platform=settings.LINUX_APT)
696 self.assertTrue(distro_only)
697 self.assertIn(self.distro_only_disable, distro_only_help)
698 self.assertNotIn(self.ppa_disable, ppa_help)
699
700+ def test_not_apt(self):
701+ # If the platform is not LINUX_APT then distro_only should be False
702+ # and the help is suppressed.
703+ for platform in (settings.LINUX_RPM, settings.WINDOWS, settings.OSX):
704+ distro_only, dist_only_help, ppa_help = manage._get_packaging_info(
705+ 'dontcare', platform=platform)
706+ self.assertTrue(distro_only)
707+ self.assertEqual(argparse.SUPPRESS, dist_only_help)
708+ self.assertEqual(argparse.SUPPRESS, ppa_help)
709+
710
711 class TestValidateBundle(
712 helpers.BundleFileTestsMixin, helpers.UrlReadTestsMixin,
713@@ -610,6 +623,16 @@
714 if exit_called:
715 mock_exit.assert_called_once_with(0)
716
717+ def test_unsupported_os(self):
718+ # get_platform raises UnsupportedOS, causing early ProgramExit.
719+ path = 'quickstart.manage.platform_support.get_platform'
720+ err = platform_support.UnsupportedOS('Unknown TRS-80')
721+ with mock.patch(path, side_effect=err):
722+ with self.assertRaises(app.ProgramExit) as ctx:
723+ manage.setup()
724+ expected = b'juju-quickstart: error: Unknown TRS-80'
725+ self.assertEqual(expected, bytes(ctx.exception))
726+
727 def test_help(self):
728 # The program help message is properly formatted.
729 with mock.patch('sys.stdout') as mock_stdout:
730@@ -689,10 +712,12 @@
731 @mock.patch('__builtin__.print', mock.Mock())
732 class TestRun(unittest.TestCase):
733
734+ juju_command = '/sbin/juju'
735+
736 def make_options(self, **kwargs):
737 """Set up the options to be passed to the run function."""
738 options = {
739- 'admin_secret': 'Secret!',
740+ 'admin_secret': 'Secretz!',
741 'bundle': None,
742 'bundle_id': None,
743 'charm_url': None,
744@@ -734,14 +759,19 @@
745 # Make mock_app.create_auth_token return a fake authentication token.
746 mock_app.create_auth_token.return_value = 'AUTHTOKEN'
747 options = self.make_options()
748- manage.run(options)
749+ with mock.patch('quickstart.manage.platform_support.get_juju_command',
750+ side_effect=[self.juju_command]):
751+ manage.run(options)
752 mock_app.ensure_dependencies.assert_called()
753 mock_app.ensure_ssh_keys.assert_called()
754 mock_app.bootstrap.assert_called_once_with(
755- options.env_name, requires_sudo=False, debug=options.debug)
756- mock_app.get_api_url.assert_called_once_with(options.env_name)
757+ options.env_name, self.juju_command, requires_sudo=False,
758+ debug=options.debug)
759+ mock_app.get_api_url.assert_called_once_with(
760+ options.env_name, self.juju_command)
761 mock_app.connect.assert_has_calls([
762- mock.call(mock_app.get_api_url(), options.admin_secret),
763+ mock.call(
764+ mock_app.get_api_url(), options.admin_secret),
765 mock.call().close(),
766 mock.call('wss://1.2.3.4:443/ws', options.admin_secret),
767 mock.call().close(),
768@@ -811,9 +841,13 @@
769 'cs:precise/juju-gui-42', '0', None, None)
770 for version in versions:
771 mock_app.ensure_dependencies.return_value = version
772- manage.run(options)
773+ with mock.patch(
774+ 'quickstart.manage.platform_support.get_juju_command',
775+ side_effect=[self.juju_command]):
776+ manage.run(options)
777 mock_app.bootstrap.assert_called_once_with(
778- options.env_name, requires_sudo=False, debug=options.debug)
779+ options.env_name, self.juju_command, requires_sudo=False,
780+ debug=options.debug)
781 mock_app.bootstrap.reset_mock()
782
783 def test_local_provider_requiring_sudo(self, mock_app, mock_open):
784@@ -831,9 +865,13 @@
785 'cs:trusty/juju-gui-42', '0', None, None)
786 for version in versions:
787 mock_app.ensure_dependencies.return_value = version
788- manage.run(options)
789+ with mock.patch(
790+ 'quickstart.manage.platform_support.get_juju_command',
791+ side_effect=[self.juju_command]):
792+ manage.run(options)
793 mock_app.bootstrap.assert_called_once_with(
794- options.env_name, requires_sudo=True, debug=options.debug)
795+ options.env_name, self.juju_command, requires_sudo=True,
796+ debug=options.debug)
797 mock_app.bootstrap.reset_mock()
798
799 def test_no_local_no_sudo(self, mock_app, mock_open):
800@@ -847,9 +885,12 @@
801 # where to deploy the charm, the service and unit data.
802 mock_app.check_environment.return_value = (
803 'cs:precise/juju-gui-42', '0', None, None)
804- manage.run(options)
805+ with mock.patch('quickstart.manage.platform_support.get_juju_command',
806+ side_effect=[self.juju_command]):
807+ manage.run(options)
808 mock_app.bootstrap.assert_called_once_with(
809- options.env_name, requires_sudo=False, debug=options.debug)
810+ options.env_name, self.juju_command,
811+ requires_sudo=False, debug=options.debug)
812
813 def test_no_browser(self, mock_app, mock_open):
814 # It is possible to avoid opening the GUI in the browser.
815
816=== modified file 'quickstart/tests/test_platform_support.py'
817--- quickstart/tests/test_platform_support.py 2014-05-29 20:49:20 +0000
818+++ quickstart/tests/test_platform_support.py 2014-06-02 14:34:25 +0000
819@@ -44,23 +44,23 @@
820 with self.patch_platform_system('Linux'):
821 with self.patch_isfile([True]):
822 result = platform_support.get_platform()
823- self.assertEqual(platform_support.LINUX_APT, result)
824+ self.assertEqual(settings.LINUX_APT, result)
825
826 def test_linux_rpm(self):
827 with self.patch_platform_system('Linux'):
828 with self.patch_isfile([False, True]):
829 result = platform_support.get_platform()
830- self.assertEqual(platform_support.LINUX_RPM, result)
831+ self.assertEqual(settings.LINUX_RPM, result)
832
833 def test_osx(self):
834 with self.patch_platform_system('Darwin'):
835 result = platform_support.get_platform()
836- self.assertEqual(platform_support.OSX, result)
837+ self.assertEqual(settings.OSX, result)
838
839 def test_windows(self):
840 with self.patch_platform_system('Windows'):
841 result = platform_support.get_platform()
842- self.assertEqual(platform_support.WINDOWS, result)
843+ self.assertEqual(settings.WINDOWS, result)
844
845 def test_unsupported_raises_exception(self):
846 with self.patch_platform_system('CP/M'):
847@@ -91,50 +91,38 @@
848
849 def test_support_local(self):
850 expected = {
851- platform_support.LINUX_APT: True,
852- platform_support.LINUX_RPM: True,
853- platform_support.OSX: False,
854- platform_support.WINDOWS: False,
855+ settings.LINUX_APT: True,
856+ settings.LINUX_RPM: True,
857+ settings.OSX: False,
858+ settings.WINDOWS: False,
859 object(): False,
860 }
861 for key, value in expected.items():
862 self.assertEqual(value, platform_support.supports_local(key))
863
864
865-class TestGetPlatformInstaller(unittest.TestCase):
866+class TestGetJujuInstaller(unittest.TestCase):
867
868 def test_linux_apt(self):
869- expected = platform_support.LINUX_APT
870- with mock.patch('quickstart.platform_support.get_platform',
871- side_effect=[expected]):
872- platform, installer = platform_support.get_platform_installer()
873- self.assertEqual(expected, platform)
874+ platform = settings.LINUX_APT
875+ installer = platform_support.get_juju_installer(platform)
876 self.assertEqual(platform_support._installer_apt, installer)
877
878 def test_osx(self):
879- expected = platform_support.OSX
880- # The JUJU_CMD should be the default location.
881- self.assertEqual('/usr/bin/juju', settings.JUJU_CMD)
882- with mock.patch('quickstart.platform_support.get_platform',
883- side_effect=[expected]):
884- platform, installer = platform_support.get_platform_installer()
885- self.assertEqual(expected, platform)
886+ platform = settings.OSX
887+ installer = platform_support.get_juju_installer(platform)
888 self.assertEqual(platform_support._installer_osx, installer)
889- # The JUJU_CMD should be changed as a side-effect.
890- self.assertEqual('/usr/local/bin/juju', settings.JUJU_CMD)
891
892 def test_linux_rpm(self):
893- expected = platform_support.LINUX_RPM
894- with mock.patch('quickstart.platform_support.get_platform',
895- side_effect=[expected]):
896- platform, installer = platform_support.get_platform_installer()
897- self.assertEqual(expected, platform)
898- self.assertIsNone(installer)
899+ platform = settings.LINUX_RPM
900+ with self.assertRaises(platform_support.UnsupportedOS) as ctx:
901+ platform_support.get_juju_installer(platform)
902+ self.assertEqual(
903+ 'No installer found for host platform.', ctx.exception.message)
904
905 def test_windows(self):
906- expected = platform_support.WINDOWS
907- with mock.patch('quickstart.platform_support.get_platform',
908- side_effect=[expected]):
909- platform, installer = platform_support.get_platform_installer()
910- self.assertEqual(expected, platform)
911- self.assertIsNone(installer)
912+ platform = settings.WINDOWS
913+ with self.assertRaises(platform_support.UnsupportedOS) as ctx:
914+ platform_support.get_juju_installer(platform)
915+ self.assertEqual(
916+ 'No installer found for host platform.', ctx.exception.message)
917
918=== modified file 'quickstart/tests/test_utils.py'
919--- quickstart/tests/test_utils.py 2014-05-30 09:23:00 +0000
920+++ quickstart/tests/test_utils.py 2014-06-02 14:34:25 +0000
921@@ -759,17 +759,17 @@
922 def test_return_deconstructed_version(self):
923 # Should return a deconstructed juju version.
924 with self.patch_call(0, '1.17.1-precise-amd64\n', ''):
925- version = utils.get_juju_version()
926+ version = utils.get_juju_version('juju')
927 self.assertEqual((1, 17, 1), version)
928
929 def test_juju_version_error(self):
930 # A ValueError is raised if "juju version" exits with an error.
931 with self.patch_call(1, 'foo', 'bad wolf'):
932 with self.assert_value_error('bad wolf'):
933- utils.get_juju_version()
934+ utils.get_juju_version('juju')
935
936 def test_invalid_version_string(self):
937 # A ValueError is raised if "juju version" outputs an invalid version.
938 with self.patch_call(0, '1.17-precise-amd64', ''):
939 with self.assert_value_error('invalid version string: 1.17'):
940- utils.get_juju_version()
941+ utils.get_juju_version('juju')
942
943=== modified file 'quickstart/utils.py'
944--- quickstart/utils.py 2014-05-29 18:07:12 +0000
945+++ quickstart/utils.py 2014-06-02 14:34:25 +0000
946@@ -338,7 +338,7 @@
947 return parse_status_output(output, ['machines', '0', 'series'])
948
949
950-def get_juju_version():
951+def get_juju_version(juju_command):
952 """Return the current juju-core version.
953
954 Return a (major:int, minor:int, patch:int) tuple, including major, minor
955@@ -347,7 +347,7 @@
956 Raise a ValueError if the "juju version" call exits with an error
957 or the returned version is not well formed.
958 """
959- retcode, output, error = call(settings.JUJU_CMD, 'version')
960+ retcode, output, error = call(juju_command, 'version')
961 if retcode:
962 raise ValueError(error.encode('utf-8'))
963 version_string = output.split('-')[0]

Subscribers

People subscribed via source and target branches