Merge lp:~bac/charms/precise/juju-gui/constraint-parsing-deux into lp:~juju-gui/charms/precise/juju-gui/trunk

Proposed by Brad Crittenden
Status: Merged
Merged at revision: 171
Proposed branch: lp:~bac/charms/precise/juju-gui/constraint-parsing-deux
Merge into: lp:~juju-gui/charms/precise/juju-gui/trunk
Diff against target: 227 lines (+33/-104)
5 files modified
HACKING.md (+4/-0)
server-requirements.pip (+1/-0)
server/guiserver/bundles/utils.py (+3/-41)
server/guiserver/tests/bundles/test_utils.py (+22/-62)
tests/20-functional.test (+3/-1)
To merge this branch: bzr merge lp:~bac/charms/precise/juju-gui/constraint-parsing-deux
Reviewer Review Type Date Requested Status
charmers Pending
Review via email: mp+209327@code.launchpad.net

Description of the change

Constraint parsing from charmworldlib.

The constraint parsing code has been moved to charmworldlib so it has been
deleted here.

Add dependency on charmworldlib and include it in deps.

https://codereview.appspot.com/71230043/

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

Reviewers: mp+209327_code.launchpad.net,

Message:
Please take a look.

Description:
Constraint parsing from charmworldlib.

The constraint parsing code has been moved to charmworldlib so it has
been
deleted here.

Add dependency on charmworldlib and include it in deps.

https://code.launchpad.net/~bac/charms/precise/juju-gui/constraint-parsing-deux/+merge/209327

(do not edit description out of merge proposal)

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

Affected files (+32, -102 lines):
   M HACKING.md
   A [revision details]
   A deps/charmworldlib-0.3.0.tar.gz
   M server-requirements.pip
   M server/guiserver/bundles/utils.py
   M server/guiserver/tests/bundles/test_utils.py
   M tests/20-functional.test

Revision history for this message
Francesco Banconi (frankban) wrote :

Nice branch Brad, thank you!
LGTM with minor changes.

https://codereview.appspot.com/71230043/diff/1/HACKING.md
File HACKING.md (right):

https://codereview.appspot.com/71230043/diff/1/HACKING.md#newcode72
HACKING.md:72: Note that the test will not work using an LXC
environment. The test
Thank you for this! To be more explicit, we could say "Note that
functional tests will not work...".

https://codereview.appspot.com/71230043/diff/1/server/guiserver/bundles/utils.py
File server/guiserver/bundles/utils.py (left):

https://codereview.appspot.com/71230043/diff/1/server/guiserver/bundles/utils.py#oldcode40
server/guiserver/bundles/utils.py:40: # Define a sequence of allowed
constraints to be used in the process of
I am very happy that now we have a shared lib at least for this
constraints stuff.

https://codereview.appspot.com/71230043/diff/1/server/guiserver/bundles/utils.py
File server/guiserver/bundles/utils.py (right):

https://codereview.appspot.com/71230043/diff/1/server/guiserver/bundles/utils.py#newcode34
server/guiserver/bundles/utils.py:34: from charmworldlib.utils import
parse_constraints
Please keep those in alphabetical order.

https://codereview.appspot.com/71230043/diff/1/server/guiserver/bundles/utils.py#newcode160
server/guiserver/bundles/utils.py:160: - the bundle includes unsupported
constraints.
I see that parse_constraints raises a ValueError if constraints are not
valid. Maybe it's worth mentioning here too, or below, e.g.
- the bundle includes unsupported or invalid constraints.

We also might want to mention somewhere (perhaps in the requirements
file) that when updating the charmworldlib tgz, we should ensure
parse_constraints still only raises ValueErrors.

https://codereview.appspot.com/71230043/

176. By Brad Crittenden

Documentation changes per review.

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

*** Submitted:

Constraint parsing from charmworldlib.

The constraint parsing code has been moved to charmworldlib so it has
been
deleted here.

Add dependency on charmworldlib and include it in deps.

R=frankban
CC=
https://codereview.appspot.com/71230043

https://codereview.appspot.com/71230043/diff/1/HACKING.md
File HACKING.md (right):

https://codereview.appspot.com/71230043/diff/1/HACKING.md#newcode72
HACKING.md:72: Note that the test will not work using an LXC
environment. The test
On 2014/03/05 13:07:54, frankban wrote:
> Thank you for this! To be more explicit, we could say "Note that
functional
> tests will not work...".

Done.

https://codereview.appspot.com/71230043/diff/1/server/guiserver/bundles/utils.py
File server/guiserver/bundles/utils.py (left):

https://codereview.appspot.com/71230043/diff/1/server/guiserver/bundles/utils.py#oldcode40
server/guiserver/bundles/utils.py:40: # Define a sequence of allowed
constraints to be used in the process of
On 2014/03/05 13:07:54, frankban wrote:
> I am very happy that now we have a shared lib at least for this
constraints
> stuff.

Done.

https://codereview.appspot.com/71230043/diff/1/server/guiserver/bundles/utils.py
File server/guiserver/bundles/utils.py (right):

https://codereview.appspot.com/71230043/diff/1/server/guiserver/bundles/utils.py#newcode34
server/guiserver/bundles/utils.py:34: from charmworldlib.utils import
parse_constraints
On 2014/03/05 13:07:54, frankban wrote:
> Please keep those in alphabetical order.

Done.

https://codereview.appspot.com/71230043/diff/1/server/guiserver/bundles/utils.py#newcode160
server/guiserver/bundles/utils.py:160: - the bundle includes unsupported
constraints.
On 2014/03/05 13:07:54, frankban wrote:
> I see that parse_constraints raises a ValueError if constraints are
not valid.
> Maybe it's worth mentioning here too, or below, e.g.
> - the bundle includes unsupported or invalid constraints.

> We also might want to mention somewhere (perhaps in the requirements
file) that
> when updating the charmworldlib tgz, we should ensure
parse_constraints still
> only raises ValueErrors.

I made the first change. The reminder seems like a good idea but would
not scale. I mean we have lots of dependencies so making notes about
double checking exceptions seems unworkable.

https://codereview.appspot.com/71230043/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'HACKING.md'
2--- HACKING.md 2013-11-19 16:31:00 +0000
3+++ HACKING.md 2014-03-05 14:55:46 +0000
4@@ -69,6 +69,10 @@
5 your `~/.juju/environments.yaml`, that will be bootstrapped before running the
6 tests and destroyed at the end of the test run.
7
8+Note that the functional tests will not work using an LXC environment. The
9+test co-locates the juju-gui on the bootstrap node, which is not possible in
10+LXC.
11+
12 Please read further for additional details.
13
14 ### Unit Tests ###
15
16=== added file 'deps/charmworldlib-0.3.0.tar.gz'
17Binary files deps/charmworldlib-0.3.0.tar.gz 1970-01-01 00:00:00 +0000 and deps/charmworldlib-0.3.0.tar.gz 2014-03-05 14:55:46 +0000 differ
18=== modified file 'server-requirements.pip'
19--- server-requirements.pip 2014-02-20 13:08:59 +0000
20+++ server-requirements.pip 2014-03-05 14:55:46 +0000
21@@ -28,3 +28,4 @@
22 websocket-client==0.12.0
23 jujuclient==0.15
24 juju-deployer==0.3.2
25+charmworldlib==0.3.0
26
27=== modified file 'server/guiserver/bundles/utils.py'
28--- server/guiserver/bundles/utils.py 2014-02-12 19:11:20 +0000
29+++ server/guiserver/bundles/utils.py 2014-03-05 14:55:46 +0000
30@@ -29,6 +29,7 @@
31 )
32 from tornado.httpclient import AsyncHTTPClient
33
34+from charmworldlib.utils import parse_constraints
35 from guiserver.watchers import AsyncWatcher
36 from jujuclient import EnvError
37
38@@ -37,23 +38,6 @@
39 STARTED = 'started'
40 CANCELLED = 'cancelled'
41 COMPLETED = 'completed'
42-# Define a sequence of allowed constraints to be used in the process of
43-# preparing the bundle object. See the _prepare_constraints function below.
44-ALLOWED_CONSTRAINTS = (
45- 'arch',
46- 'container',
47- 'cpu-cores',
48- 'cpu-power',
49- 'mem',
50- 'root-disk',
51- # XXX: BradCrittenden 2014-02-12:
52- # tags are supported by MaaS only so they are not currently implemented.
53- # It is unclear whether the GUI should support them or not so they are
54- # being left out for now.
55- # Also, tags are a comma-separated, which would clash with the currently
56- # broken constraint parsing in the GUI.
57- # 'tags',
58-)
59
60
61 def create_change(deployment_id, status, queue=None, error=None):
62@@ -162,28 +146,6 @@
63 logging.info('deployment {} completed'.format(deployment_id))
64
65
66-def _prepare_constraints(constraints):
67- """Validate and prepare the given service constraints.
68-
69- If constraints are passed as a string, convert them to be a dict.
70-
71- Return the validated constraints dict.
72- Raise a ValueError if unsupported constraints are present.
73- """
74- if not isinstance(constraints, collections.Mapping):
75- try:
76- constraints = dict(i.split('=') for i in constraints.split(','))
77- except ValueError:
78- # A ValueError is raised if constraints are invalid, e.g. "cpu=,".
79- raise ValueError('invalid constraints: {}'.format(constraints))
80- unsupported = set(constraints).difference(ALLOWED_CONSTRAINTS)
81- if unsupported:
82- msg = 'unsupported constraints: {}'.format(
83- ', '.join(sorted(unsupported)))
84- raise ValueError(msg)
85- return constraints
86-
87-
88 def prepare_bundle(bundle):
89 """Validate and prepare the bundle.
90
91@@ -195,7 +157,7 @@
92 Raise a ValueError if:
93 - the bundle is not well structured;
94 - the bundle does not include services;
95- - the bundle includes unsupported constraints.
96+ - the bundle includes unsupported or invalid constraints.
97 """
98 # XXX frankban 2013-11-07: is the GUI Server in charge of validating the
99 # bundles? For now, the weak checks below should be enough.
100@@ -213,7 +175,7 @@
101 del service_data['constraints']
102 else:
103 # Otherwise sanitize the value.
104- service_data['constraints'] = _prepare_constraints(constraints)
105+ service_data['constraints'] = parse_constraints(constraints)
106
107
108 def require_authenticated_user(view):
109
110=== modified file 'server/guiserver/tests/bundles/test_utils.py'
111--- server/guiserver/tests/bundles/test_utils.py 2014-02-12 13:55:47 +0000
112+++ server/guiserver/tests/bundles/test_utils.py 2014-03-05 14:55:46 +0000
113@@ -269,70 +269,30 @@
114 self.assertTrue(watcher.closed)
115
116
117-class TestPrepareConstraints(unittest.TestCase):
118-
119- def test_valid_constraints(self):
120- # Valid constraints are returned as they are.
121- constraints = {
122- 'arch': 'i386',
123- 'cpu-cores': 4,
124- 'cpu-power': 2,
125- 'mem': 2000,
126- 'root-disk': '1G',
127- 'container': 'lxc',
128- }
129- self.assertEqual(constraints, utils._prepare_constraints(constraints))
130-
131- def test_valid_constraints_subset(self):
132- # A subset of valid constraints is returned as it is.
133- constraints = {'cpu-cores': '4', 'cpu-power': 2}
134- self.assertEqual(constraints, utils._prepare_constraints(constraints))
135-
136- def test_invalid_constraints(self):
137- # A ValueError is raised if unsupported constraints are found.
138- with self.assertRaises(ValueError) as context_manager:
139- utils._prepare_constraints({'arch': 'i386', 'not-valid': 'bang!'})
140- self.assertEqual(
141- 'unsupported constraints: not-valid',
142- str(context_manager.exception))
143-
144- def test_string_constraints(self):
145- # String constraints are converted to a dict.
146- constraints = 'arch=i386,cpu-cores=4,cpu-power=2,mem=2000'
147- expected = {
148- 'arch': 'i386',
149- 'cpu-cores': '4',
150- 'cpu-power': '2',
151- 'mem': '2000',
152- }
153- self.assertEqual(expected, utils._prepare_constraints(constraints))
154-
155- def test_string_constraints_subset(self):
156- # A subset of string constraints is converted to a dict.
157- constraints = 'cpu-cores=4,mem=2000'
158- expected = {'cpu-cores': '4', 'mem': '2000'}
159- self.assertEqual(expected, utils._prepare_constraints(constraints))
160-
161- def test_unsupported_string_constraints(self):
162- # A ValueError is raised if unsupported string constraints are found.
163- with self.assertRaises(ValueError) as context_manager:
164- utils._prepare_constraints('cpu-cores=4,invalid1=1,invalid2=2')
165- self.assertEqual(
166- 'unsupported constraints: invalid1, invalid2',
167- str(context_manager.exception))
168-
169- def test_invalid_string_constraints(self):
170- # A ValueError is raised if unsupported string constraints are found.
171- with self.assertRaises(ValueError) as context_manager:
172- utils._prepare_constraints('arch=,cpu-cores=,')
173- self.assertEqual(
174- 'invalid constraints: arch=,cpu-cores=,',
175- str(context_manager.exception))
176-
177-
178 class TestPrepareBundle(unittest.TestCase):
179
180- def test_constraints_conversion(self):
181+ def test_constraints_conversion_space_separated(self):
182+ # Service constraints stored as strings are converted to a dict.
183+ bundle = {
184+ 'services': {
185+ 'django': {'constraints': 'arch=i386 cpu-cores=4 mem=2000'},
186+ },
187+ }
188+ expected = {
189+ 'services': {
190+ 'django': {
191+ 'constraints': {
192+ 'arch': 'i386',
193+ 'cpu-cores': '4',
194+ 'mem': '2000',
195+ },
196+ },
197+ },
198+ }
199+ utils.prepare_bundle(bundle)
200+ self.assertEqual(expected, bundle)
201+
202+ def test_constraints_conversion_comma_separated(self):
203 # Service constraints stored as strings are converted to a dict.
204 bundle = {
205 'services': {
206
207=== modified file 'tests/20-functional.test'
208--- tests/20-functional.test 2014-01-16 20:32:55 +0000
209+++ tests/20-functional.test 2014-03-05 14:55:46 +0000
210@@ -86,6 +86,7 @@
211 # Create a Selenium browser instance.
212 selenium = self.selenium = Firefox()
213 self.addCleanup(selenium.quit)
214+ self.service_name = None
215 super(DeployTestMixin, self).setUp()
216
217 def assertEnvironmentIsConnected(self):
218@@ -170,7 +171,8 @@
219 class TestDeployOptions(DeployTestMixin, unittest.TestCase):
220
221 def tearDown(self):
222- juju_destroy_service(self.service_name)
223+ if self.service_name is not None:
224+ juju_destroy_service(self.service_name)
225
226 def test_stable_release(self):
227 # Ensure the stable Juju GUI release is correctly set up.

Subscribers

People subscribed via source and target branches