Merge lp:~fwereade/pyjuju/constraints-compatibility into lp:pyjuju

Proposed by William Reade
Status: Merged
Approved by: Kapil Thangavelu
Approved revision: 522
Merged at revision: 524
Proposed branch: lp:~fwereade/pyjuju/constraints-compatibility
Merge into: lp:pyjuju
Diff against target: 222 lines (+29/-79)
3 files modified
juju/machine/constraints.py (+7/-33)
juju/machine/tests/test_constraints.py (+18/-46)
juju/providers/common/tests/test_launch.py (+4/-0)
To merge this branch: bzr merge lp:~fwereade/pyjuju/constraints-compatibility
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+101560@code.launchpad.net

Description of the change

Forward-compatibility fixes for Constraints

* Any constraint name can now be registered; no more global registry
* Unrecognised constraints are now ignored but persisted when introduced via
  ConstraintSet.load
* Unrecognised traits are now always errors when introduced via
  ConstraintSet.parse

https://codereview.appspot.com/6001050/

To post a comment you must log in.
Revision history for this message
William Reade (fwereade) wrote :

Reviewers: mp+101560_code.launchpad.net,

Message:
Please take a look.

Description:
Forward-compatibility fixes for Constraints

* Any constraint name can now be registered; no more global registry
* Unrecognised constraints are now ignored but persisted when introduced
via
   ConstraintSet.load
* Unrecognised traits are now always errors when introduced via
   ConstraintSet.parse

https://code.launchpad.net/~fwereade/juju/constraints-compatibility/+merge/101560

(do not edit description out of merge proposal)

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

Affected files:
   A [revision details]
   M juju/machine/constraints.py
   M juju/machine/tests/test_constraints.py
   M juju/providers/common/tests/test_launch.py

Revision history for this message
Kapil Thangavelu (hazmat) wrote :
Revision history for this message
William Reade (fwereade) wrote :

*** Submitted:

Forward-compatibility fixes for Constraints

* Any constraint name can now be registered; no more global registry
* Unrecognised constraints are now ignored but persisted when introduced
via
   ConstraintSet.load
* Unrecognised traits are now always errors when introduced via
   ConstraintSet.parse

R=hazmat
CC=
https://codereview.appspot.com/6001050

https://codereview.appspot.com/6001050/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'juju/machine/constraints.py'
2--- juju/machine/constraints.py 2012-03-30 08:41:02 +0000
3+++ juju/machine/constraints.py 2012-04-11 14:08:26 +0000
4@@ -1,25 +1,8 @@
5-import logging
6 import operator
7 from UserDict import DictMixin
8
9 from juju.errors import ConstraintError, UnknownConstraintError
10
11-log = logging.getLogger("juju.machine.constraints")
12-
13-# To allow providers to construct ConstraintSets which silently ignore known-
14-# but-inapplicable constraints (ec2-zone on orchestra, for example), we keep
15-# a hardcoded global registry here. It will not be hard to remember to update
16-# this when working with providers, because _ConstraintTypes with unknown
17-# names cannot be created and will cause test failures so long as the added
18-# constraint registration code is exercised in the provider tests.
19-ALL_NAMES = (
20- "ubuntu-series", "provider-type",
21- "arch", "cpu", "mem", "instance-type",
22- "ec2-zone",
23- "maas-name",
24- "orchestra-classes",
25-)
26-
27
28 def _dont_convert(s):
29 return s
30@@ -42,7 +25,6 @@
31 """
32
33 def __init__(self, name, default, converter, comparer, visible):
34- assert name in ALL_NAMES, "please update ALL_NAMES"
35 self.name = name
36 self.default = default
37 self._converter = converter
38@@ -150,15 +132,9 @@
39 def get(self, name):
40 """Get the (internal) _ConstraintType object corresponding to `name`.
41
42- If `name` is known, but not registered in this ConstraintSet, None will
43- be returned; if the constraint name is entirely unknown, an
44- UnknownConstraintError will be raised.
45+ Returns None if no _ConstraintType has been registered under that name.
46 """
47- try:
48- return self._registry[name]
49- except KeyError:
50- if name not in ALL_NAMES:
51- raise UnknownConstraintError(name)
52+ return self._registry.get(name)
53
54 def parse(self, strs):
55 """Create a Constraints from strings (as used on the command line)"""
56@@ -168,12 +144,7 @@
57 name, value = s.split("=", 1)
58 constraint = self.get(name)
59 if constraint is None:
60- # A constraint called name does exist for some provider (if
61- # it didn't exist anywhere, .get would have raised) but is
62- # not relevant for this provider.
63- log.warn(
64- "ignored irrelevant %r constraint", name)
65- continue
66+ raise UnknownConstraintError(name)
67 if value == "any":
68 value = None
69 if value == "":
70@@ -204,7 +175,10 @@
71 def load(self, data):
72 """Convert a data dict to a Constraints"""
73 for k, v in data.items():
74- self.get(k).convert(v)
75+ constraint = self.get(k)
76+ if constraint is not None:
77+ # Include all of data; validate those parts we know how to.
78+ constraint.convert(v)
79 return Constraints(self, data)
80
81
82
83=== modified file 'juju/machine/tests/test_constraints.py'
84--- juju/machine/tests/test_constraints.py 2012-03-29 10:27:30 +0000
85+++ juju/machine/tests/test_constraints.py 2012-04-11 14:08:26 +0000
86@@ -27,6 +27,12 @@
87 all_providers = ["dummy", "ec2", "orchestra"]
88
89
90+def _raiser(exc_type):
91+ def raise_(s):
92+ raise exc_type(s)
93+ return raise_
94+
95+
96 class ConstraintsTestCase(TestCase):
97
98 def assert_error(self, message, *raises_args):
99@@ -219,48 +225,19 @@
100
101 class ConstraintSetTest(TestCase):
102
103- def allow_names(self, *names):
104- all_names = ["ubuntu-series", "provider-type"]
105- all_names.extend(names)
106- from juju.machine import constraints
107- self.patch(constraints, "ALL_NAMES", all_names)
108-
109- def test_register_unknown_name(self):
110- e = self.assertRaises(
111- AssertionError, ConstraintSet("provider").register, "nonsense")
112- self.assertEquals(str(e), "please update ALL_NAMES")
113-
114- def test_register_known_names(self):
115- self.allow_names("foo", "blob")
116- cs = ConstraintSet("provider")
117- cs.register("foo")
118- cs.register("blob")
119- c1 = cs.parse(["foo=bar"]).with_series("series")
120- self.assertEquals(c1["foo"], "bar")
121- self.assertEquals(c1["blob"], None)
122- c2 = cs.parse(["foo=bar"]).with_series("series")
123- self.assertTrue(c1.can_satisfy(c2))
124- self.assertTrue(c2.can_satisfy(c1))
125-
126 def test_unregistered_name(self):
127- self.allow_names("foo", "bar", "baz")
128 cs = ConstraintSet("provider")
129 cs.register("bar")
130- output = self.capture_logging()
131- constraints = cs.parse(["foo=1", "bar=2", "baz=3"])
132- self.assertIn("ignored irrelevant 'foo' constraint", output.getvalue())
133- self.assertIn("ignored irrelevant 'baz' constraint", output.getvalue())
134- self.assertEquals(constraints["bar"], "2")
135+ e = self.assertRaises(ConstraintError, cs.parse, ["bar=2", "baz=3"])
136+ self.assertEquals(str(e), "Unknown constraint: 'baz'")
137
138 def test_register_invisible(self):
139- self.allow_names("foo")
140 cs = ConstraintSet("provider")
141 cs.register("foo", visible=False)
142 e = self.assertRaises(ConstraintError, cs.parse, ["foo=bar"])
143 self.assertEquals(str(e), "Cannot set computed constraint: 'foo'")
144
145 def test_register_comparer(self):
146- self.allow_names("foo")
147 cs = ConstraintSet("provider")
148 cs.register("foo", comparer=operator.ne)
149 c1 = cs.parse(["foo=bar"]).with_series("series")
150@@ -272,7 +249,6 @@
151 self.assertTrue(c3.can_satisfy(c1))
152
153 def test_register_default_and_converter(self):
154- self.allow_names("foo")
155 cs = ConstraintSet("provider")
156 cs.register("foo", default="star", converter=lambda s: "death-" + s)
157 c1 = cs.parse([])
158@@ -281,18 +257,13 @@
159 self.assertEquals(c1["foo"], "death-clock")
160
161 def test_convert_wraps_ValueError(self):
162- self.allow_names("foo", "bar")
163-
164- def raiser(e):
165- raise e
166 cs = ConstraintSet("provider")
167- cs.register("foo", converter=lambda s: raiser(ValueError(s)))
168- cs.register("bar", converter=lambda s: raiser(KeyError(s)))
169+ cs.register("foo", converter=_raiser(ValueError))
170+ cs.register("bar", converter=_raiser(KeyError))
171 self.assertRaises(ConstraintError, cs.parse, ["foo=1"])
172 self.assertRaises(KeyError, cs.parse, ["bar=1"])
173
174 def test_register_conflicts(self):
175- self.allow_names("foo", "bar", "baz", "qux")
176 cs = ConstraintSet("provider")
177 cs.register("foo")
178 cs.register("bar")
179@@ -394,13 +365,14 @@
180 self.assertEquals(c8["mem"], 512.0)
181 self.assertEquals(c8["instance-type"], None)
182
183- def test_load_validates(self):
184- self.allow_names("foo")
185+ def test_load_validates_known(self):
186 cs = ConstraintSet("provider")
187-
188- def blam(s):
189- raise ValueError(s)
190-
191- cs.register("foo", converter=blam)
192+ cs.register("foo", converter=_raiser(ValueError))
193 e = self.assertRaises(ConstraintError, cs.load, {"foo": "bar"})
194 self.assertEquals(str(e), "Bad 'foo' constraint 'bar': bar")
195+
196+ def test_load_preserves_unknown(self):
197+ cs = ConstraintSet("provider")
198+ constraints = cs.load({"foo": "bar"})
199+ self.assertNotIn("foo", constraints)
200+ self.assertEquals(constraints.data, {"foo": "bar"})
201
202=== modified file 'juju/providers/common/tests/test_launch.py'
203--- juju/providers/common/tests/test_launch.py 2012-03-27 22:46:44 +0000
204+++ juju/providers/common/tests/test_launch.py 2012-04-11 14:08:26 +0000
205@@ -4,6 +4,7 @@
206
207 from juju.errors import EnvironmentNotFound
208 from juju.lib.testing import TestCase
209+from juju.machine.tests.test_constraints import dummy_cs
210 from juju.providers.common.base import MachineProviderBase
211 from juju.providers.common.launch import LaunchMachine
212 from juju.providers.dummy import DummyMachine, FileStorage
213@@ -22,6 +23,9 @@
214 def get_file_storage(self):
215 return self._file_storage
216
217+ def get_constraint_set(self):
218+ return succeed(dummy_cs)
219+
220 def get_zookeeper_machines(self):
221 if zookeeper:
222 return succeed([zookeeper])

Subscribers

People subscribed via source and target branches

to status/vote changes: