Merge lp:~frankban/juju-quickstart/bundle-urls into lp:juju-quickstart
- bundle-urls
- Merge into trunk
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 59 | ||||||||
Proposed branch: | lp:~frankban/juju-quickstart/bundle-urls | ||||||||
Merge into: | lp:juju-quickstart | ||||||||
Diff against target: |
440 lines (+226/-47) 7 files modified
README.rst (+2/-2) quickstart/__init__.py (+24/-1) quickstart/manage.py (+51/-25) quickstart/settings.py (+4/-1) quickstart/tests/test_manage.py (+23/-0) quickstart/tests/test_utils.py (+103/-16) quickstart/utils.py (+19/-2) |
||||||||
To merge this branch: | bzr merge lp:~frankban/juju-quickstart/bundle-urls | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju GUI Hackers | Pending | ||
Review via email: mp+211057@code.launchpad.net |
Commit message
Description of the change
Improve bundle URLs support.
Add support for promulgated bundle short names.
Add support for jujucharms bundle URLs.
Improve help messages.
Tests: `make check`.
QA:
Run `.venv/bin/python juju-quickstart -h`
and please check the spelling of the whole
help message. Also ensure it is nicely
printed on a small terminal (e.g. 80x24).
Now let's check bundle errors: all the commands
below should return a pertinent error message:
.venv/bin/python juju-quickstart bundle:
.venv/bin/python juju-quickstart https:/
.venv/bin/python juju-quickstart https:/
.venv/bin/python juju-quickstart https:/
.venv/bin/python juju-quickstart bundle:
.venv/bin/python juju-quickstart bundle:
Deploy bundles, destroy the environment after each command.
Deploy the promulgated mediawiki single bundle:
.venv/bin/python juju-quickstart bundle:
Deploy the mediawiki scalable bundle:
.venv/bin/python juju-quickstart bundle:
Deploy a bundle using its jujucharms URLs:
.venv/bin/python juju-quickstart https:/
Deploy a bundle using direct HTTPS:
.venv/bin/python juju-quickstart https:/
Francesco Banconi (frankban) wrote : | # |
Richard Harding (rharding) wrote : | # |
Thanks for this, mainly reviewed the docs. A couple of suggestions. Let
me know if any of the comments are unclear.
https:/
File quickstart/
https:/
quickstart/
HTTP(S) URLs or the charm store,
URLs <comma>
https:/
quickstart/
environment visually.
into the Juju GUI. (I'd drop the rest)
https:/
File quickstart/
https:/
quickstart/
with "bundle:"\n'
Can we word this to start with the easy example that's recommended by
moving the promulgated bundle up here.
Then word it more that "You can also specify a user bundle, and a
specific revision...
This way we push the promoted and simple case and fall back to any
person's work.
Brad Crittenden (bac) wrote : | # |
Code LGTM
https:/
File quickstart/
https:/
quickstart/
possible).
When not possible isn't there an additional machine?
https:/
File quickstart/
https:/
quickstart/
I'd move this second comment to the actual location it happens.
https:/
File quickstart/
https:/
quickstart/
set up when a jujucharms bumdle URL
typo: bundle
- 64. By Francesco Banconi
-
Changes as per review.
Francesco Banconi (frankban) wrote : | # |
Please take a look.
https:/
File quickstart/
https:/
quickstart/
possible).
On 2014/03/14 14:51:20, bac wrote:
> When not possible isn't there an additional machine?
Yes, when using local envs a new machine is created. Not sure if we
should specify it in this context.
https:/
quickstart/
HTTP(S) URLs or the charm store,
On 2014/03/14 14:37:08, rharding wrote:
> URLs <comma>
Done.
https:/
quickstart/
environment visually.
On 2014/03/14 14:37:08, rharding wrote:
> into the Juju GUI. (I'd drop the rest)
Done.
https:/
File quickstart/
https:/
quickstart/
On 2014/03/14 14:51:20, bac wrote:
> I'd move this second comment to the actual location it happens.
Done.
https:/
quickstart/
with "bundle:"\n'
On 2014/03/14 14:37:08, rharding wrote:
> Can we word this to start with the easy example that's recommended by
moving the
> promulgated bundle up here.
> Then word it more that "You can also specify a user bundle, and a
specific
> revision...
> This way we push the promoted and simple case and fall back to any
person's
> work.
Done.
https:/
File quickstart/
https:/
quickstart/
set up when a jujucharms bumdle URL
On 2014/03/14 14:51:20, bac wrote:
> typo: bundle
Done.
Brad Crittenden (bac) wrote : | # |
QA good. Thanks.
Francesco Banconi (frankban) wrote : | # |
*** Submitted:
Improve bundle URLs support.
Add support for promulgated bundle short names.
Add support for jujucharms bundle URLs.
Improve help messages.
Tests: `make check`.
QA:
Run `.venv/bin/python juju-quickstart -h`
and please check the spelling of the whole
help message. Also ensure it is nicely
printed on a small terminal (e.g. 80x24).
Now let's check bundle errors: all the commands
below should return a pertinent error message:
.venv/bin/python juju-quickstart bundle:
.venv/bin/python juju-quickstart
https:/
.venv/bin/python juju-quickstart
https:/
.venv/bin/python juju-quickstart
https:/
.venv/bin/python juju-quickstart bundle:
.venv/bin/python juju-quickstart bundle:
Deploy bundles, destroy the environment after each command.
Deploy the promulgated mediawiki single bundle:
.venv/bin/python juju-quickstart bundle:
Deploy the mediawiki scalable bundle:
.venv/bin/python juju-quickstart bundle:
Deploy a bundle using its jujucharms URLs:
.venv/bin/python juju-quickstart
https:/
Deploy a bundle using direct HTTPS:
.venv/bin/python juju-quickstart
https:/
R=rharding, bac
CC=
https:/
Francesco Banconi (frankban) wrote : | # |
Thank you Rick and Brad!
Preview Diff
1 | === modified file 'README.rst' |
2 | --- README.rst 2014-01-29 15:25:06 +0000 |
3 | +++ README.rst 2014-03-14 18:27:29 +0000 |
4 | @@ -13,14 +13,14 @@ |
5 | session. |
6 | * The Juju GUI is automatically installed, adding no additional machines |
7 | (installing on an existing state server when possible). |
8 | -* Bundles can be deployed, from local files, HTTP(S) URLs or the charm store, |
9 | +* Bundles can be deployed, from local files, HTTP(S) URLs, or the charm store, |
10 | so that a complete topology of services can be set up in one simple command. |
11 | * Quickstart ends by opening the browser and automatically logging the user |
12 | into the GUI, to observe and manage the environment visually. |
13 | * Users with a running Juju environment can run the quickstart command again to |
14 | simply re-open the GUI without having to find the proper URL and password. |
15 | |
16 | -To install and start Juju Quickstart, run the following:: |
17 | +To start Juju Quickstart, run the following:: |
18 | |
19 | juju-quickstart [-i] |
20 | |
21 | |
22 | === modified file 'quickstart/__init__.py' |
23 | --- quickstart/__init__.py 2014-03-13 11:56:58 +0000 |
24 | +++ quickstart/__init__.py 2014-03-14 18:27:29 +0000 |
25 | @@ -22,7 +22,30 @@ |
26 | from __future__ import unicode_literals |
27 | |
28 | |
29 | -VERSION = (1, 1, 3) |
30 | +FEATURES = """ |
31 | +Features include the following: |
32 | + |
33 | +* New users are guided, as needed, to install Juju, set up SSH keys, and |
34 | + configure it for first use. |
35 | +* Juju environments can be created and managed from a command line interactive |
36 | + session. |
37 | +* The Juju GUI is automatically installed, adding no additional machines |
38 | + (installing on an existing state server when possible). |
39 | +* Bundles can be deployed, from local files, HTTP(S) URLs, or the charm store, |
40 | + so that a complete topology of services can be set up in one simple command. |
41 | +* Quickstart ends by opening the browser and automatically logging the user |
42 | + into the Juju GUI. |
43 | +* Users with a running Juju environment can run the quickstart command again to |
44 | + simply re-open the GUI without having to find the proper URL and password. |
45 | + |
46 | +To start Juju Quickstart, run the following: |
47 | + |
48 | + juju-quickstart [-i] |
49 | + |
50 | +Once Juju has been installed, the command can also be run as a juju plugin, |
51 | +without the hyphen ("juju quickstart"). |
52 | +""" |
53 | +VERSION = (1, 2, 0) |
54 | |
55 | |
56 | def get_version(): |
57 | |
58 | === modified file 'quickstart/manage.py' |
59 | --- quickstart/manage.py 2014-03-11 12:47:44 +0000 |
60 | +++ quickstart/manage.py 2014-03-14 18:27:29 +0000 |
61 | @@ -65,13 +65,15 @@ |
62 | """ |
63 | bundle = options.bundle |
64 | bundle_id = None |
65 | - if bundle.startswith('bundle:'): |
66 | - # Convert "bundle:" URLs into HTTPS ones. The next if block below will |
67 | - # then load the bundle contents from the remote location. |
68 | + jujucharms_prefix = settings.JUJUCHARMS_BUNDLE_URL |
69 | + if bundle.startswith('bundle:') or bundle.startswith(jujucharms_prefix): |
70 | + # Convert "bundle:" or jujucharms.com URLs into Charmworld HTTPS ones. |
71 | try: |
72 | bundle, bundle_id = utils.convert_bundle_url(bundle) |
73 | except ValueError as err: |
74 | return parser.error('unable to open the bundle: {}'.format(err)) |
75 | + # The next if block below will then load the bundle contents from the |
76 | + # remote location. |
77 | if bundle.startswith('http://') or bundle.startswith('https://'): |
78 | # Load the bundle from a remote URL. |
79 | try: |
80 | @@ -319,47 +321,66 @@ |
81 | if default_env_name is not None: |
82 | env_help = '{} (%(default)s)'.format(env_help) |
83 | # Create and set up the arguments parser. |
84 | - parser = argparse.ArgumentParser(description=quickstart.__doc__) |
85 | + parser = argparse.ArgumentParser( |
86 | + description=quickstart.__doc__, epilog=quickstart.FEATURES, |
87 | + formatter_class=argparse.RawTextHelpFormatter) |
88 | + # Note: since we use the RawTextHelpFormatter, when adding/changing options |
89 | + # make sure the help text is nicely displayed on small 80 columns terms. |
90 | parser.add_argument( |
91 | 'bundle', default=None, nargs='?', |
92 | - help='The optional bundle to be deployed. The bundle can be ' |
93 | - '1) a fully qualified bundle URL (starting with "bundle:"), ' |
94 | - '2) a URL ("http:" or "https:") to a YAML/JSON, ' |
95 | - '3) a path to a YAML/JSON file, or ' |
96 | - '4) a path to a directory containing a "bundles.yaml" file') |
97 | + help='The optional bundle to be deployed. The bundle can be:\n' |
98 | + '1) a fully qualified bundle URL, starting with "bundle:"\n' |
99 | + ' e.g. "bundle:mediawiki/single".\n' |
100 | + ' Non promulgated bundles can be requested providing\n' |
101 | + ' the user, e.g. "bundle:~user/mediawiki/single".\n' |
102 | + ' A specific bundle revision can also be requested,\n' |
103 | + ' e.g. "bundle:~myuser/mediawiki/42/single".\n' |
104 | + ' If not specified, the last bundle revision is used;\n' |
105 | + '2) a jujucharms bundle URL, starting with\n' |
106 | + ' "{jujucharm}", e.g.\n' |
107 | + ' "{jujucharm}~user/wiki/1/simple/".\n' |
108 | + ' As seen above, jujucharms bundle URLs can also be\n' |
109 | + ' shortened, e.g.\n' |
110 | + ' "{jujucharm}mediawiki/scalable/";\n' |
111 | + '3) a URL ("http:" or "https:") to a YAML/JSON, e.g.\n' |
112 | + ' "https://raw.github.com/user/my/master/bundles.yaml";\n' |
113 | + '4) a local path to a YAML/JSON file;\n' |
114 | + '5) a path to a directory containing a "bundles.yaml"\n' |
115 | + ' file'.format(jujucharm=settings.JUJUCHARMS_BUNDLE_URL)) |
116 | parser.add_argument( |
117 | '-e', '--environment', default=default_env_name, dest='env_name', |
118 | help=env_help) |
119 | parser.add_argument( |
120 | '-n', '--bundle-name', default=None, dest='bundle_name', |
121 | - help='The name of the bundle to use. This must be included in the ' |
122 | - 'provided bundle YAML/JSON. Specifying the bundle name is not ' |
123 | - 'required if the bundle YAML/JSON only contains one bundle. This ' |
124 | - 'option is ignored if the bundle file is not specified') |
125 | + help='The name of the bundle to use.\n' |
126 | + 'This must be included in the provided bundle YAML/JSON.\n' |
127 | + 'Specifying the bundle name is not required if the\n' |
128 | + 'bundle YAML/JSON only contains one bundle. This option\n' |
129 | + 'is ignored if the bundle file is not specified') |
130 | parser.add_argument( |
131 | '-i', '--interactive', action='store_true', dest='interactive', |
132 | help='Start the environments management interactive session') |
133 | parser.add_argument( |
134 | '--environments-file', dest='env_file', |
135 | default=os.path.join(settings.JUJU_HOME, 'environments.yaml'), |
136 | - help='The path to the Juju environments YAML file (%(default)s)') |
137 | + help='The path to the Juju environments YAML file\n(%(default)s)') |
138 | parser.add_argument( |
139 | '--gui-charm-url', dest='charm_url', |
140 | - help='The Juju GUI charm URL to deploy in the environment. If not ' |
141 | - 'provided, the last release of the GUI will be deployed. The ' |
142 | - 'charm URL must include the charm version, e.g. ' |
143 | - 'cs:~juju-gui/precise/juju-gui-116. This option is ignored if ' |
144 | - 'Juju GUI is already present in the environment') |
145 | + help='The Juju GUI charm URL to deploy in the environment.\n' |
146 | + 'If not provided, the last release of the GUI will be\n' |
147 | + 'deployed. The charm URL must include the charm version,\n' |
148 | + 'e.g. "cs:~juju-gui/precise/juju-gui-162". This option is\n' |
149 | + 'ignored if the GUI is already present in the environment') |
150 | parser.add_argument( |
151 | '--no-browser', action='store_false', dest='open_browser', |
152 | - help='Avoid opening the browser to the GUI at the end of the process') |
153 | + help='Avoid opening the browser to the GUI at the end of the\nprocess') |
154 | parser.add_argument( |
155 | '--version', action='version', version='%(prog)s {}'.format(version)) |
156 | parser.add_argument( |
157 | '--debug', action='store_true', |
158 | - help='Turn debug mode on. When enabled, all the subcommands and API ' |
159 | - 'calls are logged to stdout, and the Juju environment is ' |
160 | - 'bootstrapped passing --debug') |
161 | + help='Turn debug mode on. When enabled, all the subcommands\n' |
162 | + 'and API calls are logged to stdout, and the Juju\n' |
163 | + 'environment is bootstrapped passing --debug') |
164 | # This is required by juju-core: see "juju help plugins". |
165 | parser.add_argument( |
166 | '--description', action=_DescriptionAction, default=argparse.SUPPRESS, |
167 | @@ -382,6 +403,10 @@ |
168 | def run(options): |
169 | """Run the application.""" |
170 | print('juju quickstart v{}'.format(version)) |
171 | + if options.bundle is not None: |
172 | + print('contents loaded for bundle {} (services: {})'.format( |
173 | + options.bundle_name, len(options.bundle_services))) |
174 | + |
175 | logging.debug('ensuring juju and lxc are installed') |
176 | juju_version = app.ensure_dependencies() |
177 | |
178 | @@ -431,7 +456,7 @@ |
179 | address = app.watch(env, unit_name) |
180 | env.close() |
181 | url = 'https://{}'.format(address) |
182 | - print('url: {}\npassword: {}'.format(url, admin_secret)) |
183 | + print('\nJuju GUI URL: {}\npassword: {}\n'.format(url, admin_secret)) |
184 | gui_api_url = 'wss://{}:443/ws'.format(address) |
185 | print('connecting to the Juju GUI server') |
186 | gui_env = app.connect(gui_api_url, admin_secret) |
187 | @@ -447,7 +472,8 @@ |
188 | app.deploy_bundle( |
189 | gui_env, options.bundle_yaml, options.bundle_name, |
190 | options.bundle_id) |
191 | - print('bundle deployment request accepted') |
192 | + print('bundle deployment request accepted\n' |
193 | + 'use the GUI to check the bundle deployment progress') |
194 | |
195 | if options.open_browser: |
196 | token = app.create_auth_token(gui_env) |
197 | |
198 | === modified file 'quickstart/settings.py' |
199 | --- quickstart/settings.py 2014-03-12 10:31:34 +0000 |
200 | +++ quickstart/settings.py 2014-03-14 18:27:29 +0000 |
201 | @@ -26,11 +26,14 @@ |
202 | |
203 | # The default Juju GUI charm URL to use when it is not possible to retrieve it |
204 | # from the charmworld API, e.g. due to temporary connection/charmworld errors. |
205 | -DEFAULT_CHARM_URL = 'cs:precise/juju-gui-85' |
206 | +DEFAULT_CHARM_URL = 'cs:precise/juju-gui-86' |
207 | |
208 | # The quickstart app short description. |
209 | DESCRIPTION = 'set up a Juju environment (including the GUI) in very few steps' |
210 | |
211 | +# The URL namespace for bundles in jujucharms.com. |
212 | +JUJUCHARMS_BUNDLE_URL = 'https://jujucharms.com/bundle/' |
213 | + |
214 | # The path to the Juju command. |
215 | JUJU_CMD = '/usr/bin/juju' |
216 | |
217 | |
218 | === modified file 'quickstart/tests/test_manage.py' |
219 | --- quickstart/tests/test_manage.py 2014-03-11 12:34:06 +0000 |
220 | +++ quickstart/tests/test_manage.py 2014-03-14 18:27:29 +0000 |
221 | @@ -108,6 +108,21 @@ |
222 | ['mysql', 'wordpress'], sorted(options.bundle_services)) |
223 | self.assertEqual(open(bundle_file).read(), options.bundle_yaml) |
224 | |
225 | + def test_resulting_options_from_jujucharms_url(self): |
226 | + # The options object is correctly set up when a jujucharms bundle URL |
227 | + # is passed. |
228 | + bundle_file = self.make_bundle_file() |
229 | + url = settings.JUJUCHARMS_BUNDLE_URL + 'my/bundle/' |
230 | + options = self.make_options(url, bundle_name='bundle1') |
231 | + with self.patch_urlread(contents=self.valid_bundle) as mock_urlread: |
232 | + manage._validate_bundle(options, self.parser) |
233 | + mock_urlread.assert_called_once_with( |
234 | + 'https://manage.jujucharms.com/bundle/~charmers/my/bundle/json') |
235 | + self.assertEqual('bundle1', options.bundle_name) |
236 | + self.assertEqual( |
237 | + ['mysql', 'wordpress'], sorted(options.bundle_services)) |
238 | + self.assertEqual(open(bundle_file).read(), options.bundle_yaml) |
239 | + |
240 | def test_resulting_options_from_dir(self): |
241 | # The options object is correctly set up when a bundle dir is passed. |
242 | bundle_dir = self.make_bundle_dir() |
243 | @@ -175,6 +190,14 @@ |
244 | self.parser.error.assert_called_once_with( |
245 | 'unable to open the bundle: invalid bundle URL: bundle:') |
246 | |
247 | + def test_jujucharms_url_error(self): |
248 | + # A parser error is invoked if an invalid jujucharms URL is provided. |
249 | + url = settings.JUJUCHARMS_BUNDLE_URL + 'no-such' |
250 | + options = self.make_options(url) |
251 | + manage._validate_bundle(options, self.parser) |
252 | + self.parser.error.assert_called_once_with( |
253 | + 'unable to open the bundle: invalid bundle URL: {}'.format(url)) |
254 | + |
255 | def test_error_parsing_bundle_contents(self): |
256 | # A parser error is invoked if an error occurs parsing the bundle YAML. |
257 | bundle_file = self.make_bundle_file() |
258 | |
259 | === modified file 'quickstart/tests/test_utils.py' |
260 | --- quickstart/tests/test_utils.py 2014-03-11 12:34:06 +0000 |
261 | +++ quickstart/tests/test_utils.py 2014-03-14 18:27:29 +0000 |
262 | @@ -189,28 +189,115 @@ |
263 | |
264 | class TestConvertBundleUrl(helpers.ValueErrorTestsMixin, unittest.TestCase): |
265 | |
266 | - def test_conversion(self): |
267 | - # The HTTPS location to the YAML contents are correctly returned. |
268 | + def test_full_bundle_url(self): |
269 | + # The HTTPS location to the YAML contents is correctly returned. |
270 | bundle_url = 'bundle:~myuser/wiki-bundle/42/wiki' |
271 | - expected = ( |
272 | - 'https://manage.jujucharms.com/bundle/' |
273 | - '~myuser/wiki-bundle/42/wiki/json', |
274 | - '~myuser/wiki-bundle/42/wiki', |
275 | - ) |
276 | - self.assertEqual(expected, utils.convert_bundle_url(bundle_url)) |
277 | + url, bundle_id = utils.convert_bundle_url(bundle_url) |
278 | + self.assertEqual( |
279 | + 'https://manage.jujucharms.com' |
280 | + '/bundle/~myuser/wiki-bundle/42/wiki/json', url) |
281 | + self.assertEqual('~myuser/wiki-bundle/42/wiki', bundle_id) |
282 | |
283 | - def test_right_strip(self): |
284 | + def test_bundle_url_right_strip(self): |
285 | # The trailing slash in the bundle URL is removed. |
286 | bundle_url = 'bundle:~myuser/wiki-bundle/42/wiki/' |
287 | - expected = ('https://manage.jujucharms.com' |
288 | - '/bundle/~myuser/wiki-bundle/42/wiki/json', |
289 | - '~myuser/wiki-bundle/42/wiki') |
290 | - self.assertEqual(expected, utils.convert_bundle_url(bundle_url)) |
291 | + url, bundle_id = utils.convert_bundle_url(bundle_url) |
292 | + self.assertEqual( |
293 | + 'https://manage.jujucharms.com' |
294 | + '/bundle/~myuser/wiki-bundle/42/wiki/json', url) |
295 | + self.assertEqual('~myuser/wiki-bundle/42/wiki', bundle_id) |
296 | + |
297 | + def test_bundle_url_no_revision(self): |
298 | + # The bundle revision is optional. |
299 | + bundle_url = 'bundle:~myuser/wiki-bundle/wiki-simple' |
300 | + url, bundle_id = utils.convert_bundle_url(bundle_url) |
301 | + self.assertEqual( |
302 | + 'https://manage.jujucharms.com' |
303 | + '/bundle/~myuser/wiki-bundle/wiki-simple/json', url) |
304 | + self.assertEqual('~myuser/wiki-bundle/wiki-simple', bundle_id) |
305 | + |
306 | + def test_bundle_url_no_user(self): |
307 | + # If the bundle user is not specified, the bundle is assumed to be |
308 | + # promulgated and owned by "charmers". |
309 | + bundle_url = 'bundle:wiki-bundle/1/wiki' |
310 | + url, bundle_id = utils.convert_bundle_url(bundle_url) |
311 | + self.assertEqual( |
312 | + 'https://manage.jujucharms.com' |
313 | + '/bundle/~charmers/wiki-bundle/1/wiki/json', url) |
314 | + self.assertEqual('~charmers/wiki-bundle/1/wiki', bundle_id) |
315 | + |
316 | + def test_bundle_url_short_form(self): |
317 | + # A promulgated bundle URL can just include the basket and the name. |
318 | + bundle_url = 'bundle:wiki-bundle/wiki' |
319 | + url, bundle_id = utils.convert_bundle_url(bundle_url) |
320 | + self.assertEqual( |
321 | + 'https://manage.jujucharms.com' |
322 | + '/bundle/~charmers/wiki-bundle/wiki/json', url) |
323 | + self.assertEqual('~charmers/wiki-bundle/wiki', bundle_id) |
324 | + |
325 | + def test_full_jujucharms_url(self): |
326 | + # The HTTPS location to the YAML contents is correctly returned. |
327 | + url, bundle_id = utils.convert_bundle_url( |
328 | + settings.JUJUCHARMS_BUNDLE_URL + '~myuser/wiki-bundle/42/wiki') |
329 | + self.assertEqual( |
330 | + 'https://manage.jujucharms.com' |
331 | + '/bundle/~myuser/wiki-bundle/42/wiki/json', url) |
332 | + self.assertEqual('~myuser/wiki-bundle/42/wiki', bundle_id) |
333 | + |
334 | + def test_jujucharms_url_right_strip(self): |
335 | + # The trailing slash in the jujucharms URL is removed. |
336 | + url, bundle_id = utils.convert_bundle_url( |
337 | + settings.JUJUCHARMS_BUNDLE_URL + '~charmers/mediawiki/6/scalable/') |
338 | + self.assertEqual( |
339 | + 'https://manage.jujucharms.com' |
340 | + '/bundle/~charmers/mediawiki/6/scalable/json', url) |
341 | + self.assertEqual('~charmers/mediawiki/6/scalable', bundle_id) |
342 | + |
343 | + def test_jujucharms_url_no_revision(self): |
344 | + # The bundle revision is optional. |
345 | + url, bundle_id = utils.convert_bundle_url( |
346 | + settings.JUJUCHARMS_BUNDLE_URL + '~myuser/wiki/wiki-simple/') |
347 | + self.assertEqual( |
348 | + 'https://manage.jujucharms.com' |
349 | + '/bundle/~myuser/wiki/wiki-simple/json', url) |
350 | + self.assertEqual('~myuser/wiki/wiki-simple', bundle_id) |
351 | + |
352 | + def test_jujucharms_url_no_user(self): |
353 | + # If the bundle user is not specified, the bundle is assumed to be |
354 | + # promulgated and owned by "charmers". |
355 | + url, bundle_id = utils.convert_bundle_url( |
356 | + settings.JUJUCHARMS_BUNDLE_URL + 'mediawiki/42/single/') |
357 | + self.assertEqual( |
358 | + 'https://manage.jujucharms.com' |
359 | + '/bundle/~charmers/mediawiki/42/single/json', url) |
360 | + self.assertEqual('~charmers/mediawiki/42/single', bundle_id) |
361 | + |
362 | + def test_jujucharms_url_short_form(self): |
363 | + # A jujucharms URL for a promulgated bundle can just include the basket |
364 | + # and the name. |
365 | + url, bundle_id = utils.convert_bundle_url( |
366 | + settings.JUJUCHARMS_BUNDLE_URL + 'wiki-bundle/wiki/') |
367 | + self.assertEqual( |
368 | + 'https://manage.jujucharms.com' |
369 | + '/bundle/~charmers/wiki-bundle/wiki/json', url) |
370 | + self.assertEqual('~charmers/wiki-bundle/wiki', bundle_id) |
371 | |
372 | def test_error(self): |
373 | - # A ValueError is raised if the given bundle URL is not valid. |
374 | - with self.assert_value_error('invalid bundle URL: bundle:'): |
375 | - utils.convert_bundle_url('bundle:') |
376 | + # A ValueError is raised if the bundle/jujucharms URL is not valid. |
377 | + bad_urls = ( |
378 | + 'bad', 'bundle:', 'bundle:~user', 'bundle:no-such', |
379 | + 'bundle:~user/name', 'bundle:~user/basket/revision/name', |
380 | + 'bundle:basket/name//', 'bundle:basket.name/bundle.name', |
381 | + settings.JUJUCHARMS_BUNDLE_URL, |
382 | + settings.JUJUCHARMS_BUNDLE_URL + 'bad', |
383 | + settings.JUJUCHARMS_BUNDLE_URL + '~user/no-such', |
384 | + settings.JUJUCHARMS_BUNDLE_URL + '~user/basket/revision/name/', |
385 | + settings.JUJUCHARMS_BUNDLE_URL + '~user/basket/42/name/error', |
386 | + 'https://jujucharms.com/charms/mediawiki/simple/', |
387 | + ) |
388 | + for url in bad_urls: |
389 | + with self.assert_value_error('invalid bundle URL: {}'.format(url)): |
390 | + utils.convert_bundle_url(url) |
391 | |
392 | |
393 | class TestGetCharmUrl(helpers.UrlReadTestsMixin, unittest.TestCase): |
394 | |
395 | === modified file 'quickstart/utils.py' |
396 | --- quickstart/utils.py 2014-03-11 12:34:06 +0000 |
397 | +++ quickstart/utils.py 2014-03-14 18:27:29 +0000 |
398 | @@ -30,6 +30,7 @@ |
399 | import logging |
400 | import os |
401 | import pipes |
402 | +import re |
403 | import socket |
404 | import subprocess |
405 | import urllib2 |
406 | @@ -42,6 +43,18 @@ |
407 | from quickstart.models import charms |
408 | |
409 | |
410 | +# Compile the regular expression used to parse bundle URLs. |
411 | +_bundle_expression = re.compile(r""" |
412 | + # Bundle schema or bundle URL namespace on jujucharms.com. |
413 | + ^(?:bundle:|{}) |
414 | + (?:~([-\w]+)/)? # Optional user name. |
415 | + ([-\w]+)/ # Basket name. |
416 | + (?:(\d+)/)? # Optional bundle revision number. |
417 | + ([-\w]+) # Bundle name. |
418 | + /?$ # Optional trailing slash. |
419 | +""".format(settings.JUJUCHARMS_BUNDLE_URL), re.VERBOSE) |
420 | + |
421 | + |
422 | def add_apt_repository(repository): |
423 | """Add the given APT repository to the current list of APT sources. |
424 | |
425 | @@ -117,10 +130,14 @@ |
426 | |
427 | Raise a ValueError if the given URL is not a valid bundle URL. |
428 | """ |
429 | - bundle_id = bundle_url.split(':', 1)[1].rstrip('/') |
430 | - if not bundle_id: |
431 | + match = _bundle_expression.match(bundle_url) |
432 | + if match is None: |
433 | msg = 'invalid bundle URL: {}'.format(bundle_url) |
434 | raise ValueError(msg.encode('utf-8')) |
435 | + user, basket, revision, name = match.groups() |
436 | + user_part = '~charmers/' if user is None else '~{}/'.format(user) |
437 | + revision_part = '' if revision is None else '{}/'.format(revision) |
438 | + bundle_id = '{}{}/{}{}'.format(user_part, basket, revision_part, name) |
439 | return ('https://manage.jujucharms.com/bundle/{}/json'.format(bundle_id), |
440 | bundle_id) |
441 |
Reviewers: mp+211057_ code.launchpad. net,
Message:
Please take a look.
Description:
Improve bundle URLs support.
Add support for promulgated bundle short names.
Add support for jujucharms bundle URLs.
Improve help messages.
Tests: `make check`.
QA:
Run `.venv/bin/python juju-quickstart -h`
and please check the spelling of the whole
help message. Also ensure it is nicely
printed on a small terminal (e.g. 80x24).
Now let's check bundle errors: all the commands
below should return a pertinent error message:
.venv/bin/python juju-quickstart bundle: /jujucharms. com/bundle/ mediawiki/ single/ wtf /jujucharms. com/bundle/ mediawiki/ double /jujucharms. com/charms/ mediawiki/ single mediawiki/ 42/single ~frankban/ mediawiki/ single
.venv/bin/python juju-quickstart
https:/
.venv/bin/python juju-quickstart
https:/
.venv/bin/python juju-quickstart
https:/
.venv/bin/python juju-quickstart bundle:
.venv/bin/python juju-quickstart bundle:
Deploy bundles, destroy the environment after each command.
Deploy the promulgated mediawiki single bundle: ~charmers/ mediawiki/ 6/single
.venv/bin/python juju-quickstart bundle:
Deploy the mediawiki scalable bundle: mediawiki/ scalable
.venv/bin/python juju-quickstart bundle:
Deploy a bundle using its jujucharms URLs: /jujucharms. com/bundle/ ~bac/charmworld -demo/charmworl d-minimal/
.venv/bin/python juju-quickstart
https:/
Deploy a bundle using direct HTTPS: /raw.github. com/castrojo/ mongodb- bundle/ master/ bundles. yaml
.venv/bin/python juju-quickstart
https:/
https:/ /code.launchpad .net/~frankban/ juju-quickstart /bundle- urls/+merge/ 211057
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/75830045/
Affected files (+229, -46 lines): __init_ _.py manage. py settings. py tests/test_ manage. py tests/test_ utils.py
M README.rst
A [revision details]
M quickstart/
M quickstart/
M quickstart/
M quickstart/
M quickstart/
M quickstart/utils.py