Merge lp:~evarlast/juju-quickstart/which-juju into lp:juju-quickstart
- which-juju
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 96 |
Proposed branch: | lp:~evarlast/juju-quickstart/which-juju |
Merge into: | lp:juju-quickstart |
Diff against target: |
290 lines (+125/-15) 8 files modified
quickstart/app.py (+18/-0) quickstart/manage.py (+5/-4) quickstart/platform_support.py (+18/-3) quickstart/tests/test_app.py (+29/-0) quickstart/tests/test_manage.py (+25/-4) quickstart/tests/test_platform_support.py (+15/-0) quickstart/tests/test_utils.py (+8/-2) quickstart/utils.py (+7/-2) |
To merge this branch: | bzr merge lp:~evarlast/juju-quickstart/which-juju |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju GUI Hackers | Pending | ||
Review via email: mp+227238@code.launchpad.net |
Commit message
Description of the change
Set juju binary with JUJU env var
Print a warning if a customized juju is being used.
Do not continue if juju must be executed using sudo.
/usr/bin/juju was hard-coded. Made interoperability testing with
pre-release versions of juju difficult. This allows override with JUJU
environment var.
Francesco Banconi (frankban) wrote : | # |
Francesco Banconi (frankban) wrote : | # |
9 + if os.environ.
This can be written as:
juju_command = os.getenv('JUJU', '').strip()
if not juju_command:
juju_command = platform_
However, I'd like this logic to be placed in get_juju_command(), so that the function can still be potentially re-used in other parts of the code.
- 91. By Jay R. Wren
-
addressing review concerns
Francesco Banconi (frankban) wrote : | # |
Hey Jay, thanks for the work on this branch.
I don't see here the changes I requested in my initial comment.
I am still not sure about the introduction of this feature.
Maybe we should ask for another opinion.
Jay R. Wren (evarlast) wrote : | # |
I somehow missed the entire first comment. I retract my request for reconsideration.
Francesco Banconi (frankban) wrote : | # |
As discussed on IRC, let's change this to:
- print a warning if a customized juju is being used;
- ignore the env var (and output a warning) if we need to use sudo.
Also, please add a test for the get_juju_command changes.
- 92. By Jay R. Wren
-
custom juju warnings
- print a warning if a customized juju is being used;
- ignore the env var (and output a warning) if we need ot use sudo.test the get_juju_command changes.
Jay R. Wren (evarlast) wrote : | # |
Reviewers: mp+227238_
Message:
Please take a look.
Description:
/usr/bin/juju was hard-coded. Made interoperability testing with
pre-release versions of juju difficult. This allows override with JUJU
environment var.
https:/
(do not edit description out of merge proposal)
Please review this at https:/
Affected files (+50, -5 lines):
A [revision details]
M quickstart/
M quickstart/
M quickstart/
M quickstart/
Index: [revision details]
=== added file '[revision details]'
--- [revision details] 2012-01-01 00:00:00 +0000
+++ [revision details] 2012-01-01 00:00:00 +0000
@@ -0,0 +1,2 @@
+Old revision:
<email address hidden>
+New revision: <email address hidden>
Index: quickstart/
=== modified file 'quickstart/
--- quickstart/
+++ quickstart/
@@ -497,6 +497,12 @@
environment')
# If the Juju version is less than 1.17.2 then use sudo for local
envs.
+ if requires_sudo:
+ non_env_
+ options.platform, True)
+ if non_env_
+ print("Warning: ignoring JUJU environment variable")
+ juju_command = non_env_
already_
Index: quickstart/
=== modified file 'quickstart/
--- quickstart/
+++ quickstart/
@@ -110,15 +110,23 @@
}
-def get_juju_
+def get_juju_
"""Return the path to the Juju command on the given platform.
If the platform does not have a novel location, the default will be
returned.
+
+ If the environment variable JUJU is set, then its value will be
+ returned.
"""
- return settings.
- platform,
- settings.
+ juju_command = os.getenv('JUJU', '').strip()
+ if juju_command is not None and not ignore_env_var:
+ print("Warning: a customized juju is being used.")
+ return juju_command
+ else:
+ return settings.
+ platform,
+ settings.
def get_juju_
Index: quickstart/
=== modified file 'quickstart/
--- quickstart/
+++ quickstart/
@@ -898,7 +898,7 @@
with mock.patch(
...
Francesco Banconi (frankban) wrote : | # |
Thanks for this branch Jay.
Please take a look at my concerns below: if I am right then some more
work (and thinking) is required before being able to include this
functionality.
https:/
File quickstart/
https:/
quickstart/
juju_command)
We are using juju_command here to check if juju is installed and
retrieve its version. This means we are already using a possible
injected juju path. Later, if sudo is required, we end up changing the
juju command, invalidating the information retrieved here.
In theory, when switching the juju_command path, we should re-ensure its
dependencies.
For instance, assume I don't have juju installed, but I have an old
compiled version which requires sudo: with the current logic, the
dependency check pass, but the the application proceed with an invalid
juju version until it crashes because the default path is not found
(assuming I understand the code flow correctly).
It seems to me that a refactoring is required here to accomplish the
goal. Perhaps get_juju_command can return the path and a "customized"
flag? And then move the path switching logic to ensure_
you have a better idea?
https:/
quickstart/
At this point I'd just do:
if juju_version < (1, 17, 2):
requires_sudo = True
...
But as described above, this part is likely to change.
https:/
quickstart/
variable")
Please use logging.
for capitalization).
Also, this if block does not seem to be tested.
If we move the path switching logic somewhere else I presume it would be
easier to test this case.
https:/
File quickstart/
https:/
quickstart/
If the environment variable JUJU is set and ignore_env_var is False,
then its value will be returned.
https:/
quickstart/
being used.")
Please use logging.warn, e.g.:
logging.warn('a customized juju is being used: ' + juju_command)
https:/
quickstart/
Unnecessary else block.
https:/
File quickstart/
https:/
quickstart/
mock_app, mock_open):
Very nice test.
- 93. By Jay R. Wren
-
unnecessary if block
Jay R. Wren (evarlast) wrote : | # |
On 2014/08/21 16:06:06, frankban wrote:
> Thanks for this branch Jay.
> Please take a look at my concerns below: if I am right then some more
work (and
> thinking) is required before being able to include this functionality.
> https:/
> File quickstart/
https:/
> quickstart/
juju_command)
> We are using juju_command here to check if juju is installed and
retrieve its
> version. This means we are already using a possible injected juju
path. Later,
> if sudo is required, we end up changing the juju command, invalidating
the
> information retrieved here.
> In theory, when switching the juju_command path, we should re-ensure
its
> dependencies.
> For instance, assume I don't have juju installed, but I have an old
compiled
> version which requires sudo: with the current logic, the dependency
check pass,
> but the the application proceed with an invalid juju version until it
crashes
> because the default path is not found (assuming I understand the code
flow
> correctly).
> It seems to me that a refactoring is required here to accomplish the
goal.
> Perhaps get_juju_command can return the path and a "customized" flag?
And then
> move the path switching logic to ensure_
better idea?
I agree with all of the issues you have raised. I feel that addressing
them now is out of scope of the original intent of the change, which was
for debugging purposes for an expert experienced user, not the user who
needs dependencies for juju resolved.
https:/
> quickstart/
> At this point I'd just do:
> if juju_version < (1, 17, 2):
> requires_sudo = True
> ...
> But as described above, this part is likely to change.
https:/
> quickstart/
variable")
> Please use logging.
for
> capitalization).
> Also, this if block does not seem to be tested.
> If we move the path switching logic somewhere else I presume it would
be easier
> to test this case.
The cover tool is either wrong when it says 100% or
test_manage.
https:/
> File quickstart/
https:/
> quickstart/
> If the environment variable JUJU is set and ignore_env_var is False,
then its
> value will be returned.
https:/
> quickstart/
is being
> used.")
> Please use logging.warn, e.g.:
> logging.warn('a customized juju is being used: ' + juju_command)
Will this get ...
Jay R. Wren (evarlast) wrote : | # |
Please take a look.
- 94. By Jay R. Wren
-
use logging.warn instead of print
- 95. By Jay R. Wren
-
re-ensure dependencies when JUJU unused
When skipping the JUJU in environment variable because sudo is required, check the dependencies again.
Add the common no JUJU environment variable test case.
Jay R. Wren (evarlast) wrote : | # |
Please take a look.
Francesco Banconi (frankban) wrote : | # |
On 2014/08/21 17:33:59, jrwren wrote:
> I agree with all of the issues you have raised. I feel that addressing
them now
> is out of scope of the original intent of the change, which was for
debugging
> purposes for an expert experienced user, not the user who needs
dependencies for
> juju resolved.
I am not sure we want to achieve this goal while introducing a subtle
bug.
I'd propose to simplify this, by not allowing quickstart to be run at
all if sudo is
required and a customized command is provided. I guess the main goal is
to
allow people to run quickstart on a newer compiled version of Juju, and
so
this should not be a problem.
A possible solution is like the following (code not tested).
1) Make get_juju_command return a customized flag, assuming that flag is
true
only when JUJU is set and it differs from the default command, e.g.:
def get_juju_
"""Return the path to the Juju command on the given platform.
Also return a flag indicating whether the user requested to
customize
the Juju command by providing a JUJU environment variable.
If the platform does not have a novel location, the default will be
returned.
"""
platform_
platform,
customized
if customized_command and (customized_command != platform_command):
customized_command)
return customized_command, True
return platform_command, False
Note that in the warning we don't want to repeat "Warning:" and we don't
want
capitalization.
2) Add an app.juju_
def juju_requires_
"""Report whether the given Juju version requires "sudo".
The env_type argument is the current environment type.
Raise a ProgramExit if a customized version of Juju is being used
and Juju
itself requires "sudo".
"""
# If the Juju version is less than 1.17.2 then use sudo for local
envs.
if env_type == 'local' and juju_version < (1, 17, 2):
if customized:
raise ProgramExit(
reasons...')
return True
return False
3) At this point, the logic in manage.run is very simple, as it is
supposed
to be:
juju_command, customized = platform_
logging.
juju_version = app.ensure_
requires_sudo = app.juju_
logging.
app.
print(
if options.env_type == 'local':
# If this is a local environment, notify the user that "sudo"
will be
# required to bootstrap the application, even in newer Juju
versions
# where "sudo" is ...
- 96. By Jay R. Wren
-
refactor manage.run
extract requires_sudo
Jay R. Wren (evarlast) wrote : | # |
Please take a look.
Francesco Banconi (frankban) wrote : | # |
Very nice branch Jay.
I have some other minor suggestions, and I am well over my EOD, so I'll
QA this on Monday.
Thank you!
https:/
File quickstart/
https:/
quickstart/
Pre-existing, by could you please add a new line here separating stdlib
imports from quickstart ones?
The latter must also be put in alphabetical order.
from quickstart import (
settings,
utils,
)
https:/
quickstart/
whetehr the user requested to customize
Typo: whetehr.
https:/
quickstart/
juju_command != platform_command:
"if juju_command:" is sufficient here, an empty string evaluates to
False.
See PEP8: http://
https:/
File quickstart/
https:/
quickstart/
TestRequiresSud
TestJujuRequire
https:/
quickstart/
self.assertTrue
We could pass the version as second argument to assertTrue here, so
that, in case of failure, we know what version failed:
self.assertTrue(
app.
version)
Or similar.
https:/
quickstart/
self.assertFals
Ditto.
https:/
quickstart/
self.assertRais
test_app defines a ProgramExitTest
that the exit message is also correct, e.g.:
with self.assert_
app.
https:/
File quickstart/
https:/
quickstart/
test_getenv_
This test seems no longer valid, and we need to add a test for the case
JUJU is set to the default command (customized should be false).
Also, expected is starting to be a little be vague on those tests.
I'd read them better, for example, like:
expected_command = '/custom/juju'
with mock.patch(
command, customized = platform_supp...
- 97. By Jay R. Wren
-
pep8; convert once
Jay R. Wren (evarlast) wrote : | # |
Please take a look.
Francesco Banconi (frankban) wrote : | # |
LGTM with a couple of minor changes.
QA ok.
Thank you!
https:/
File quickstart/
https:/
quickstart/
Missing comma at the end of the line (utils,).
https:/
quickstart/
being used.")
no need for the final . here
https:/
File quickstart/
https:/
quickstart/
TestGetJujuComm
What do you think about changing from expected/actual to
expected_
I read the second one way better.
https:/
quickstart/
test_getenv_
The test name here seems to no longer describe the goal.
- 98. By Jay R. Wren
-
commas; periods; names
Jay R. Wren (evarlast) wrote : | # |
Please take a look.
https:/
File quickstart/
https:/
quickstart/
TestGetJujuComm
On 2014/08/25 14:37:39, frankban wrote:
> What do you think about changing from expected/actual to
> expected_
> I read the second one way better.
Done.
https:/
quickstart/
test_getenv_
On 2014/08/25 14:37:38, frankban wrote:
> The test name here seems to no longer describe the goal.
Done.
Brad Crittenden (bac) wrote : | # |
The branch LGTM Jay. Thanks for getting it done and working with
Francesco to improve it.
https:/
File quickstart/app.py (right):
https:/
quickstart/
type.
I haven't looked at the quickstart code in a while so I didn't
immediately know what an 'env_type' was but could guess it meant
"environment type". Then I was in the unhappy place of not knowing what
an environment type was.
Perhaps you could augment this comment with "e.g. local, ec2, azure".
- 99. By Jay R. Wren
-
document env_type in juju_requires_sudo
Jay R. Wren (evarlast) wrote : | # |
Please take a look.
- 100. By Jay R. Wren
-
merge upstream
Jay R. Wren (evarlast) wrote : | # |
Please take a look.
Jay R. Wren (evarlast) wrote : | # |
*** Submitted:
Set juju binary with JUJU env var
Print a warning if a customized juju is being used.
Do not continue if juju must be executed using sudo.
/usr/bin/juju was hard-coded. Made interoperability testing with
pre-release versions of juju difficult. This allows override with JUJU
environment var.
R=frankban, bac
CC=
https:/
Preview Diff
1 | === modified file 'quickstart/app.py' |
2 | --- quickstart/app.py 2014-08-21 16:24:05 +0000 |
3 | +++ quickstart/app.py 2014-08-25 16:57:54 +0000 |
4 | @@ -549,3 +549,21 @@ |
5 | env.deploy_bundle(bundle_yaml, name=bundle_name, bundle_id=bundle_id) |
6 | except jujuclient.EnvError as err: |
7 | raise ProgramExit('bad API server response: {}'.format(err.message)) |
8 | + |
9 | + |
10 | +def juju_requires_sudo(env_type, juju_version, customized): |
11 | + """Report whether the given Juju version requires "sudo". |
12 | + |
13 | + The env_type argument is the current environment type. |
14 | + "e.g. local, ec2, azure" |
15 | + |
16 | + Raise a ProgramExit if a customized version of Juju is being used and Juju |
17 | + itself requires "sudo" |
18 | + """ |
19 | + # If the Juju version is less than 1.17.2 then use sudo for local envs. |
20 | + if env_type == 'local' and juju_version < (1, 17, 2): |
21 | + if customized: |
22 | + raise ProgramExit(b'cannot use a customized Juju when it ' |
23 | + 'requires sudo') |
24 | + return True |
25 | + return False |
26 | |
27 | === modified file 'quickstart/manage.py' |
28 | --- quickstart/manage.py 2014-08-21 16:23:26 +0000 |
29 | +++ quickstart/manage.py 2014-08-25 16:57:54 +0000 |
30 | @@ -496,25 +496,26 @@ |
31 | print('contents loaded for bundle {} (services: {})'.format( |
32 | options.bundle_name, len(options.bundle_services))) |
33 | |
34 | - juju_command = platform_support.get_juju_command(options.platform) |
35 | + juju_command, custom_juju = platform_support.get_juju_command( |
36 | + options.platform) |
37 | |
38 | logging.debug('ensuring juju and dependencies are installed') |
39 | juju_version = app.ensure_dependencies( |
40 | options.distro_only, options.platform, juju_command) |
41 | |
42 | + requires_sudo = app.juju_requires_sudo( |
43 | + options.env_type, juju_version, custom_juju) |
44 | + |
45 | logging.debug('ensuring SSH keys are available') |
46 | app.ensure_ssh_keys() |
47 | |
48 | print('bootstrapping the {} environment (type: {})'.format( |
49 | options.env_name, options.env_type)) |
50 | - requires_sudo = False |
51 | if options.env_type == 'local': |
52 | # If this is a local environment, notify the user that "sudo" will be |
53 | # required to bootstrap the application, even in newer Juju versions |
54 | # where "sudo" is invoked by juju-core itself. |
55 | print('sudo privileges will be required to bootstrap the environment') |
56 | - # If the Juju version is less than 1.17.2 then use sudo for local envs. |
57 | - requires_sudo = juju_version < (1, 17, 2) |
58 | |
59 | already_bootstrapped, bootstrap_node_series = app.bootstrap( |
60 | options.env_name, juju_command, |
61 | |
62 | === modified file 'quickstart/platform_support.py' |
63 | --- quickstart/platform_support.py 2014-08-21 19:01:57 +0000 |
64 | +++ quickstart/platform_support.py 2014-08-25 16:57:54 +0000 |
65 | @@ -21,10 +21,14 @@ |
66 | unicode_literals, |
67 | ) |
68 | |
69 | +import logging |
70 | import os |
71 | import platform |
72 | -from quickstart import utils |
73 | -from quickstart import settings |
74 | + |
75 | +from quickstart import ( |
76 | + settings, |
77 | + utils, |
78 | +) |
79 | |
80 | |
81 | def validate_platform(pf): |
82 | @@ -113,12 +117,23 @@ |
83 | def get_juju_command(platform): |
84 | """Return the path to the Juju command on the given platform. |
85 | |
86 | + Also return a flag indicating whether the user requested to customize |
87 | + the Juju command by providing a JUJU environment variable. |
88 | + |
89 | If the platform does not have a novel location, the default will be |
90 | returned. |
91 | + |
92 | + If the environment variable JUJU is set, then its value will be |
93 | + returned. |
94 | """ |
95 | - return settings.JUJU_CMD_PATHS.get( |
96 | + juju_command = os.getenv('JUJU', '').strip() |
97 | + platform_command = settings.JUJU_CMD_PATHS.get( |
98 | platform, |
99 | settings.JUJU_CMD_PATHS['default']) |
100 | + if juju_command and juju_command != platform_command: |
101 | + logging.warn("a customized juju is being used") |
102 | + return juju_command, True |
103 | + return platform_command, False |
104 | |
105 | |
106 | def get_juju_installer(platform): |
107 | |
108 | === modified file 'quickstart/tests/test_app.py' |
109 | --- quickstart/tests/test_app.py 2014-08-01 15:35:56 +0000 |
110 | +++ quickstart/tests/test_app.py 2014-08-25 16:57:54 +0000 |
111 | @@ -1677,3 +1677,32 @@ |
112 | with self.assertRaises(ValueError) as context_manager: |
113 | app.deploy_bundle(env, self.yaml, self.name, None) |
114 | self.assertIs(error, context_manager.exception) |
115 | + |
116 | + |
117 | +class TestJujuRequiresSudo(ProgramExitTestsMixin, unittest.TestCase): |
118 | + no_sudo_versions = [ |
119 | + (1, 17, 2), (1, 17, 10), (1, 18, 0), (1, 18, 2), (2, 16, 1)] |
120 | + sudo_versions = [(0, 7, 9), (1, 0, 0), (1, 16, 42), (1, 17, 0), (1, 17, 1)] |
121 | + |
122 | + def test_non_local_returns_false(self): |
123 | + # A non-local provider does not require sudo. |
124 | + actual = app.juju_requires_sudo('aws', None, None) |
125 | + self.assertFalse(actual) |
126 | + |
127 | + def test_local_old_juju_returns_true(self): |
128 | + # A juju previous to 1.17.2 requires sudo. |
129 | + for version in self.sudo_versions: |
130 | + self.assertTrue(app.juju_requires_sudo('local', version, False), |
131 | + version) |
132 | + |
133 | + def test_local_newer_juju_returns_false(self): |
134 | + # A juju 1.17.2 and newer does not require sudo. |
135 | + for version in self.no_sudo_versions: |
136 | + self.assertFalse(app.juju_requires_sudo('local', version, False), |
137 | + version) |
138 | + |
139 | + def test_raises_programexit(self): |
140 | + for version in self.sudo_versions: |
141 | + with self.assert_program_exit('cannot use a customized Juju when ' |
142 | + 'it requires sudo'): |
143 | + app.juju_requires_sudo('local', version, True) |
144 | |
145 | === modified file 'quickstart/tests/test_manage.py' |
146 | --- quickstart/tests/test_manage.py 2014-08-01 15:35:56 +0000 |
147 | +++ quickstart/tests/test_manage.py 2014-08-25 16:57:54 +0000 |
148 | @@ -795,9 +795,10 @@ |
149 | mock_app.watch.return_value = '1.2.3.4' |
150 | # Make mock_app.create_auth_token return a fake authentication token. |
151 | mock_app.create_auth_token.return_value = 'AUTHTOKEN' |
152 | + mock_app.juju_requires_sudo.return_value = False |
153 | options = self.make_options() |
154 | with mock.patch('quickstart.manage.platform_support.get_juju_command', |
155 | - side_effect=[self.juju_command]): |
156 | + side_effect=[(self.juju_command, False)]): |
157 | manage.run(options) |
158 | mock_app.ensure_dependencies.assert_called() |
159 | mock_app.ensure_ssh_keys.assert_called() |
160 | @@ -878,11 +879,12 @@ |
161 | # where to deploy the charm, the service and unit data. |
162 | mock_app.check_environment.return_value = ( |
163 | 'cs:precise/juju-gui-42', '0', None, None) |
164 | + mock_app.juju_requires_sudo.return_value = False |
165 | for version in versions: |
166 | mock_app.ensure_dependencies.return_value = version |
167 | with mock.patch( |
168 | 'quickstart.manage.platform_support.get_juju_command', |
169 | - side_effect=[self.juju_command]): |
170 | + side_effect=[(self.juju_command, False)]): |
171 | manage.run(options) |
172 | mock_app.bootstrap.assert_called_once_with( |
173 | options.env_name, self.juju_command, requires_sudo=False, |
174 | @@ -904,11 +906,12 @@ |
175 | # where to deploy the charm, the service and unit data. |
176 | mock_app.check_environment.return_value = ( |
177 | 'cs:trusty/juju-gui-42', '0', None, None) |
178 | + mock_app.juju_requires_sudo.return_value = True |
179 | for version in versions: |
180 | mock_app.ensure_dependencies.return_value = version |
181 | with mock.patch( |
182 | 'quickstart.manage.platform_support.get_juju_command', |
183 | - side_effect=[self.juju_command]): |
184 | + side_effect=[(self.juju_command, False)]): |
185 | manage.run(options) |
186 | mock_app.bootstrap.assert_called_once_with( |
187 | options.env_name, self.juju_command, requires_sudo=True, |
188 | @@ -928,8 +931,9 @@ |
189 | # where to deploy the charm, the service and unit data. |
190 | mock_app.check_environment.return_value = ( |
191 | 'cs:precise/juju-gui-42', '0', None, None) |
192 | + mock_app.juju_requires_sudo.return_value = False |
193 | with mock.patch('quickstart.manage.platform_support.get_juju_command', |
194 | - side_effect=[self.juju_command]): |
195 | + side_effect=[(self.juju_command, False)]): |
196 | manage.run(options) |
197 | mock_app.bootstrap.assert_called_once_with( |
198 | options.env_name, self.juju_command, |
199 | @@ -1006,3 +1010,20 @@ |
200 | u'admin-secret not found in {}/environments/local.jenv ' |
201 | 'or environments.yaml'.format(settings.JUJU_HOME)) |
202 | self.assertEqual(expected, context.exception.message) |
203 | + |
204 | + def test_juju_environ_var_set(self, mock_app, mock_open): |
205 | + mock_app.bootstrap.return_value = (True, 'precise') |
206 | + mock_app.check_environment.return_value = ( |
207 | + 'cs:precise/juju-gui-42', '0', None, None) |
208 | + mock_app.juju_requires_sudo.return_value = False |
209 | + options = self.make_options(env_type='aws') |
210 | + juju_command = 'some/devel/path/juju' |
211 | + with mock.patch('os.environ', {'JUJU': juju_command}): |
212 | + manage.run(options) |
213 | + mock_app.bootstrap.assert_called_once_with( |
214 | + options.env_name, juju_command, requires_sudo=False, |
215 | + debug=options.debug, upload_tools=options.upload_tools, |
216 | + upload_series=options.upload_series, |
217 | + constraints=options.constraints) |
218 | + mock_app.get_api_url.assert_called_once_with( |
219 | + options.env_name, juju_command) |
220 | |
221 | === modified file 'quickstart/tests/test_platform_support.py' |
222 | --- quickstart/tests/test_platform_support.py 2014-06-13 14:38:05 +0000 |
223 | +++ quickstart/tests/test_platform_support.py 2014-08-25 16:57:54 +0000 |
224 | @@ -165,3 +165,18 @@ |
225 | def test_linux_apt_passes(self): |
226 | result = platform_support.validate_platform(settings.LINUX_APT) |
227 | self.assertIsNone(result) |
228 | + |
229 | + |
230 | +class TestGetJujuCommand(unittest.TestCase): |
231 | + |
232 | + def test_getenv_succeeds(self): |
233 | + expected_command = '/custom/juju' |
234 | + with mock.patch('os.environ', {'JUJU': expected_command}): |
235 | + command, customized = platform_support.get_juju_command(None) |
236 | + self.assertEqual(expected_command, command) |
237 | + self.assertTrue(customized) |
238 | + |
239 | + def test_without_env_var(self): |
240 | + expected = settings.JUJU_CMD_PATHS['default'], False |
241 | + actual = platform_support.get_juju_command('default') |
242 | + self.assertEqual(expected, actual) |
243 | |
244 | === modified file 'quickstart/tests/test_utils.py' |
245 | --- quickstart/tests/test_utils.py 2014-06-02 12:56:24 +0000 |
246 | +++ quickstart/tests/test_utils.py 2014-08-25 16:57:54 +0000 |
247 | @@ -768,8 +768,14 @@ |
248 | with self.assert_value_error('bad wolf'): |
249 | utils.get_juju_version('juju') |
250 | |
251 | + def test_alpha_version_string(self): |
252 | + # Patch level defaults to zero. |
253 | + with self.patch_call(0, '1.17-alpha1-precise-amd64', ''): |
254 | + version = utils.get_juju_version('juju') |
255 | + self.assertEqual((1, 17, 0), version) |
256 | + |
257 | def test_invalid_version_string(self): |
258 | # A ValueError is raised if "juju version" outputs an invalid version. |
259 | - with self.patch_call(0, '1.17-precise-amd64', ''): |
260 | - with self.assert_value_error('invalid version string: 1.17'): |
261 | + with self.patch_call(0, '1-precise-amd64', ''): |
262 | + with self.assert_value_error('invalid version string: 1'): |
263 | utils.get_juju_version('juju') |
264 | |
265 | === modified file 'quickstart/utils.py' |
266 | --- quickstart/utils.py 2014-06-02 12:56:24 +0000 |
267 | +++ quickstart/utils.py 2014-08-25 16:57:54 +0000 |
268 | @@ -344,6 +344,8 @@ |
269 | Return a (major:int, minor:int, patch:int) tuple, including major, minor |
270 | and patch version numbers. |
271 | |
272 | + Handle the special case of no patch level by defaulting to 0. |
273 | + |
274 | Raise a ValueError if the "juju version" call exits with an error |
275 | or the returned version is not well formed. |
276 | """ |
277 | @@ -352,8 +354,11 @@ |
278 | raise ValueError(error.encode('utf-8')) |
279 | version_string = output.split('-')[0] |
280 | try: |
281 | - major, minor, patch = version_string.split('.', 2) |
282 | - return int(major), int(minor), int(patch) |
283 | + parts = version_string.split('.', 2) |
284 | + if len(parts) == 2: |
285 | + parts.append(0) |
286 | + major, minor, patch = map(int, parts) |
287 | + return major, minor, patch |
288 | except ValueError: |
289 | msg = 'invalid version string: {}'.format(version_string) |
290 | raise ValueError(msg.encode('utf-8')) |
This branch is nice Jay, thank you.
However, this change makes me a little bit nervous, for two reasons.
1) In older versions of Juju, we require to bootstrap using sudo on local envs. Due to the fact we support those versions, that logic is indeed in quickstart. This means that, under certain circumstances, we can end up calling "sudo $JUJU". I am not convinced that the use cases for this change justify the security issues that can be be raised.
2) I like the fact that Quickstart only supports stable/packaged versions of juju (/usr/bin/juju). This means it's relatively easy to dupe possible bugs being filed.
As mentioned, I am not sure about introducing this feature, but if we decide to follow this path, I'd like quickstart to at least do the following:
- check that the given path exists and it's executable;
- expose a big WARNING to the user, informing she's using a customized juju binary, and requiring a user input in order to proceed.
I'd also like to s/$JUJU/$JUJU_CMD or similar.
What do you think?