1
3
from juju.errors import ConstraintError
2
4
from juju.lib.testing import TestCase
3
from juju.machine.constraints import Constraints
5
from juju.machine.constraints import Constraints, ConstraintSet
7
# These objects exist for the convenience of other test files
8
dummy_cs = ConstraintSet("dummy")
9
dummy_cs.register_generics([])
10
dummy_constraints = dummy_cs.parse([])
11
series_constraints = dummy_constraints.with_series("series")
6
13
generic_defaults = {
7
"arch": None, "cpu": 1, "mem": 512,
14
"arch": "amd64", "cpu": 1, "mem": 512,
8
15
"ubuntu-series": None, "provider-type": None}
9
16
dummy_defaults = dict(generic_defaults, **{
10
17
"provider-type": "dummy"})
11
18
ec2_defaults = dict(generic_defaults, **{
12
19
"provider-type": "ec2",
14
"ec2-instance-type": None})
15
orchestra_defaults = dict(generic_defaults, **{
21
"instance-type": None})
22
orchestra_defaults = {
16
23
"provider-type": "orchestra",
17
"orchestra-name": None,
18
"orchestra-classes": None})
24
"ubuntu-series": None,
25
"orchestra-classes": None}
20
27
all_providers = ["dummy", "ec2", "orchestra"]
30
def _raiser(exc_type):
22
36
class ConstraintsTestCase(TestCase):
24
38
def assert_error(self, message, *raises_args):
25
39
e = self.assertRaises(ConstraintError, *raises_args)
26
40
self.assertEquals(str(e), message)
28
def assert_roundtrip_equal(self, constraints, expected):
42
def assert_roundtrip_equal(self, cs, constraints, expected):
43
self.assertEquals(dict(constraints), expected)
29
44
self.assertEquals(constraints, expected)
30
self.assertEquals(Constraints(constraints.data), expected)
45
self.assertEquals(dict(cs.load(constraints.data)), expected)
46
self.assertEquals(cs.load(constraints.data), expected)
33
49
class ConstraintsTest(ConstraintsTestCase):
35
def test_defaults(self):
36
constraints = Constraints.from_strs("orchestra", [])
37
self.assert_roundtrip_equal(constraints, orchestra_defaults)
38
constraints = Constraints.from_strs("ec2", [])
39
self.assert_roundtrip_equal(constraints, ec2_defaults)
40
constraints = Constraints.from_strs("dummy", [])
41
self.assert_roundtrip_equal(constraints, dummy_defaults)
51
def test_equality(self):
52
self.assert_roundtrip_equal(
53
dummy_cs, dummy_constraints, dummy_defaults)
43
55
def test_complete(self):
44
incomplete_constraints = Constraints.from_strs("womble", [])
45
self.assertFalse(incomplete_constraints.complete)
56
incomplete_constraints = dummy_cs.parse([])
46
57
complete_constraints = incomplete_constraints.with_series("wandering")
47
58
self.assertTrue(complete_constraints.complete)
49
def assert_invalid(self, message, providers, *constraint_strs):
50
for provider in providers:
52
message, Constraints.from_strs, provider, constraint_strs)
54
if len(providers) != len(all_providers):
55
# Check it *is* valid for other providers
56
for provider in all_providers:
57
if provider in providers:
59
Constraints.from_strs(provider, constraint_strs)
60
def assert_invalid(self, message, *constraint_strs):
62
message, dummy_cs.parse, constraint_strs)
61
64
def test_invalid_input(self):
62
65
"""Reject nonsense constraints"""
63
66
self.assert_invalid(
64
67
"Could not interpret 'BLAH' constraint: need more than 1 value to "
66
all_providers, "BLAH")
67
70
self.assert_invalid(
68
71
"Unknown constraint: 'foo'",
69
all_providers, "foo=", "bar=")
71
74
def test_invalid_constraints(self):
72
75
"""Reject nonsensical constraint values"""
73
76
self.assert_invalid(
74
"Bad 'arch' constraint 'leg': unknown architecture", all_providers,
77
"Bad 'arch' constraint 'leg': unknown architecture",
76
79
self.assert_invalid(
77
"Bad 'cpu' constraint '-1': must be non-negative", all_providers,
80
"Bad 'cpu' constraint '-1': must be non-negative",
79
82
self.assert_invalid(
80
83
"Bad 'cpu' constraint 'fish': could not convert string to float: "
82
all_providers, "cpu=fish")
83
86
self.assert_invalid(
84
"Bad 'mem' constraint '-1': must be non-negative", all_providers,
87
"Bad 'mem' constraint '-1': must be non-negative",
86
89
self.assert_invalid(
87
90
"Bad 'mem' constraint '4P': invalid literal for float(): 4P",
88
all_providers, "mem=4P")
90
"Bad 'ec2-zone' constraint 'Q': expected lowercase ascii char",
91
["ec2"], "ec2-zone=Q")
93
93
def test_hidden_constraints(self):
94
94
"""Reject attempts to explicitly specify computed constraints"""
95
95
self.assert_invalid(
96
"Cannot set computed constraint: 'ubuntu-series'", all_providers,
96
"Cannot set computed constraint: 'ubuntu-series'",
97
97
"ubuntu-series=cheesy")
98
98
self.assert_invalid(
99
"Cannot set computed constraint: 'provider-type'", all_providers,
99
"Cannot set computed constraint: 'provider-type'",
100
100
"provider-type=dummy")
102
def test_overlap_ec2(self):
103
"""Overlapping ec2 constraints should be detected"""
105
"Ambiguous constraints: 'arch' overlaps with 'ec2-instance-type'",
106
["ec2"], "ec2-instance-type=m1.small", "arch=i386")
108
"Ambiguous constraints: 'cpu' overlaps with 'ec2-instance-type'",
109
["ec2"], "ec2-instance-type=m1.small", "cpu=1")
111
"Ambiguous constraints: 'ec2-instance-type' overlaps with 'mem'",
112
["ec2"], "ec2-instance-type=m1.small", "mem=2G")
114
def test_overlap_orchestra(self):
115
"""Overlapping orchestra constraints should be detected"""
117
"Ambiguous constraints: 'arch' overlaps with 'orchestra-name'",
118
["orchestra"], "orchestra-name=herbert", "arch=i386")
120
"Ambiguous constraints: 'cpu' overlaps with 'orchestra-name'",
121
["orchestra"], "orchestra-name=herbert", "cpu=1")
123
"Ambiguous constraints: 'mem' overlaps with 'orchestra-name'",
124
["orchestra"], "orchestra-name=herbert", "mem=2G")
126
"Ambiguous constraints: 'orchestra-classes' overlaps with "
128
["orchestra"], "orchestra-name=herbert", "orchestra-classes=x,y")
131
103
class ConstraintsUpdateTest(ConstraintsTestCase):
133
def assert_constraints(self, provider, strss, expected):
134
constraints = Constraints.from_strs(provider, strss[0])
105
def assert_constraints(self, strss, expected):
106
constraints = dummy_cs.parse(strss[0])
135
107
for strs in strss[1:]:
136
constraints.update(Constraints.from_strs(provider, strs))
137
self.assert_roundtrip_equal(constraints, expected)
139
def assert_constraints_dummy(self, strss, expected):
108
constraints.update(dummy_cs.parse(strs))
140
109
expected = dict(dummy_defaults, **expected)
141
self.assert_constraints("dummy", strss, expected)
110
self.assert_roundtrip_equal(dummy_cs, constraints, expected)
143
def test_constraints_dummy(self):
112
def test_constraints(self):
144
113
"""Sane constraints dicts are generated for unknown environments"""
145
self.assert_constraints_dummy([[]], {})
146
self.assert_constraints_dummy([["arch=arm"]], {"arch": "arm"})
147
self.assert_constraints_dummy([["cpu=0.1"]], {"cpu": 0.1})
148
self.assert_constraints_dummy([["mem=128"]], {"mem": 128})
149
self.assert_constraints_dummy(
114
self.assert_constraints([[]], {})
115
self.assert_constraints([["cpu=", "mem="]], {})
116
self.assert_constraints([["arch=arm"]], {"arch": "arm"})
117
self.assert_constraints([["cpu=0.1"]], {"cpu": 0.1})
118
self.assert_constraints([["mem=128"]], {"mem": 128})
119
self.assert_constraints([["cpu=0"]], {"cpu": 0})
120
self.assert_constraints([["mem=0"]], {"mem": 0})
121
self.assert_constraints(
150
122
[["arch=amd64", "cpu=6", "mem=1.5G"]],
151
{"arch": "amd64", "cpu": 6, "mem": 1536,})
123
{"arch": "amd64", "cpu": 6, "mem": 1536})
153
125
def test_overwriting_basic(self):
154
126
"""Later values shadow earlier values"""
155
self.assert_constraints_dummy(
127
self.assert_constraints(
156
128
[["cpu=4", "mem=512"], ["arch=i386", "mem=1G"]],
157
129
{"arch": "i386", "cpu": 4, "mem": 1024})
159
131
def test_reset(self):
160
132
"""Empty string resets to juju default"""
161
self.assert_constraints_dummy(
133
self.assert_constraints(
162
134
[["arch=arm", "cpu=4", "mem=1024"], ["arch=", "cpu=", "mem="]],
163
{"arch": None, "cpu": 1, "mem": 512})
164
self.assert_constraints_dummy(
135
{"arch": "amd64", "cpu": 1, "mem": 512})
136
self.assert_constraints(
165
137
[["arch=", "cpu=", "mem="], ["arch=arm", "cpu=4", "mem=1024"]],
166
138
{"arch": "arm", "cpu": 4, "mem": 1024})
168
def assert_constraints_ec2(self, strss, expected):
169
expected = dict(ec2_defaults, **expected)
170
self.assert_constraints("ec2", strss, expected)
172
def test_constraints_ec2(self):
173
"""Sane constraints dicts are generated for ec2"""
174
self.assert_constraints_ec2([[]], {})
175
self.assert_constraints_ec2([["arch=arm"]], {"arch": "arm"})
176
self.assert_constraints_ec2([["cpu=128"]], {"cpu": 128})
177
self.assert_constraints_ec2([["mem=2G"]], {"mem": 2048})
178
self.assert_constraints_ec2([["ec2-zone=b"]], {"ec2-zone": "b"})
179
self.assert_constraints_ec2(
180
[["arch=amd64", "cpu=32", "mem=2G", "ec2-zone=b"]],
181
{"arch": "amd64", "cpu": 32, "mem": 2048, "ec2-zone": "b"})
183
def test_overwriting_ec2_instance_type(self):
184
"""ec2-instance-type interacts correctly with arch, cpu, mem"""
185
self.assert_constraints_ec2(
186
[["ec2-instance-type=t1.micro"]],
187
{"ec2-instance-type": "t1.micro", "cpu": None, "mem": None})
188
self.assert_constraints_ec2(
189
[["arch=arm", "cpu=8"], ["ec2-instance-type=t1.micro"]],
190
{"ec2-instance-type": "t1.micro", "cpu": None, "mem": None})
191
self.assert_constraints_ec2(
192
[["ec2-instance-type=t1.micro"], ["arch=arm", "cpu=8"]],
193
{"ec2-instance-type": None, "arch": "arm", "cpu": 8, "mem": None})
195
def assert_constraints_orchestra(self, strss, expected):
196
expected = dict(orchestra_defaults, **expected)
197
self.assert_constraints("orchestra", strss, expected)
199
def test_constraints_orchestra(self):
200
"""Sane constraints dicts are generated for orchestra"""
201
self.assert_constraints_orchestra([[]], {})
202
self.assert_constraints_orchestra([["arch=arm"]], {"arch": "arm"})
203
self.assert_constraints_orchestra([["cpu=128"]], {"cpu": 128})
204
self.assert_constraints_orchestra([["mem=0.25T"]], {"mem": 262144})
205
self.assert_constraints_orchestra(
206
[["orchestra-classes=x,y"]], {"orchestra-classes": ["x", "y"]})
207
self.assert_constraints_orchestra(
208
[["arch=i386", "cpu=2", "mem=768M", "orchestra-classes=a,b"]],
209
{"arch": "i386", "cpu": 2, "mem": 768,
210
"orchestra-classes": ["a", "b"]})
212
def test_overwriting_orchestra_name(self):
213
"""orchestra-name interacts correctly with arch, cpu, mem"""
214
self.assert_constraints_orchestra(
215
[["orchestra-name=baggins"]],
216
{"orchestra-name": "baggins", "cpu": None, "mem": None})
217
self.assert_constraints_orchestra(
218
[["orchestra-name=baggins"], ["arch=arm"]],
219
{"orchestra-name": None, "arch": "arm", "cpu": None, "mem": None})
220
self.assert_constraints_orchestra(
221
[["arch=arm", "cpu=2"], ["orchestra-name=baggins"]],
222
{"orchestra-name": "baggins", "arch": None, "cpu": None,
225
def test_overwriting_orchestra_classes(self):
226
"""orchestra-classes interacts correctly with orchestra-name"""
227
self.assert_constraints_orchestra(
228
[["orchestra-name=baggins"], ["orchestra-classes=c,d", "cpu=2"]],
229
{"orchestra-classes": ["c", "d"], "cpu": 2, "mem": None})
230
self.assert_constraints_orchestra(
231
[["orchestra-classes=zz,top", "arch=amd64", "cpu=2"],
232
["orchestra-name=baggins"]],
233
{"orchestra-name": "baggins", "orchestra-classes": None,
234
"cpu": None, "mem": None})
237
141
class ConstraintsFulfilmentTest(ConstraintsTestCase):
239
def completed_constraints(self, provider="provider", series="series"):
240
return Constraints.from_strs(provider, []).with_series(series)
242
def assert_incomparable(self, c1, c2):
244
"Cannot compare incomplete constraints", c1.can_satisfy, c2)
246
"Cannot compare incomplete constraints", c2.can_satisfy, c1)
248
143
def assert_match(self, c1, c2, expected):
249
144
self.assertEquals(c1.can_satisfy(c2), expected)
250
145
self.assertEquals(c2.can_satisfy(c1), expected)
252
147
def test_fulfil_completeness(self):
253
"""can_satisfy needs to be called on and with complete Constraints~s"""
254
c1 = Constraints({"provider-type": "a"})
255
c2 = Constraints({"provider-type": "a"})
256
self.assert_incomparable(c1, c2)
257
c3 = self.completed_constraints("a")
258
self.assert_incomparable(c1, c3)
259
c4 = self.completed_constraints("a")
260
self.assert_match(c3, c4, True)
149
can_satisfy needs to be called on and with complete Constraints~s to
150
have any chance of working.
153
dummy_cs, {"provider-type": "dummy", "ubuntu-series": "x"})
154
self.assert_match(good, good, True)
155
bad = [Constraints(dummy_cs, {}),
156
Constraints(dummy_cs, {"provider-type": "dummy"}),
157
Constraints(dummy_cs, {"ubuntu-series": "x"})]
158
for i, bad1 in enumerate(bad):
159
self.assert_match(bad1, good, False)
161
self.assert_match(bad1, bad2, False)
262
163
def test_fulfil_matches(self):
164
other_cs = ConstraintSet("other")
165
other_cs.register_generics([])
166
other_constraints = other_cs.parse([])
264
Constraints({"provider-type": "a", "ubuntu-series": "x"}),
265
Constraints({"provider-type": "a", "ubuntu-series": "y"}),
266
Constraints({"provider-type": "b", "ubuntu-series": "x"}),
267
Constraints({"provider-type": "b", "ubuntu-series": "y"}))
168
dummy_constraints.with_series("x"),
169
dummy_constraints.with_series("y"),
170
other_constraints.with_series("x"),
171
other_constraints.with_series("y"))
269
173
for i, c1 in enumerate(instances):
270
174
self.assert_match(c1, c1, True)
272
176
self.assert_match(c1, c2, False)
274
178
def assert_can_satisfy(
275
self, machine_strs, unit_strs, expected, provider="provider"):
276
machine = Constraints.from_strs(provider, machine_strs)
179
self, machine_strs, unit_strs, expected):
180
machine = dummy_cs.parse(machine_strs)
277
181
machine = machine.with_series("shiny")
278
unit = Constraints.from_strs(provider, unit_strs)
182
unit = dummy_cs.parse(unit_strs)
279
183
unit = unit.with_series("shiny")
280
184
self.assertEquals(machine.can_satisfy(unit), expected)
282
if provider != "provider":
283
# Check that a different provider doesn't detect any problem,
284
# because it's ignoring all off-provider constraints.
285
machine = Constraints.from_strs("provider", machine_strs)
286
machine = machine.with_series("shiny")
287
unit = Constraints.from_strs("provider", unit_strs)
288
unit = unit.with_series("shiny")
289
self.assertEquals(machine.can_satisfy(unit), True)
292
186
def test_can_satisfy(self):
293
187
self.assert_can_satisfy([], [], True)
295
self.assert_can_satisfy(["arch=arm"], [], True)
189
self.assert_can_satisfy(["arch=arm"], [], False)
190
self.assert_can_satisfy(["arch=amd64"], [], True)
296
191
self.assert_can_satisfy([], ["arch=arm"], False)
297
192
self.assert_can_satisfy(["arch=i386"], ["arch=arm"], False)
298
193
self.assert_can_satisfy(["arch=arm"], ["arch=amd64"], False)
299
194
self.assert_can_satisfy(["arch=amd64"], ["arch=amd64"], True)
195
self.assert_can_satisfy(["arch=i386"], ["arch=any"], True)
196
self.assert_can_satisfy(["arch=arm"], ["arch=any"], True)
197
self.assert_can_satisfy(["arch=amd64"], ["arch=any"], True)
198
self.assert_can_satisfy(["arch=any"], ["arch=any"], True)
199
self.assert_can_satisfy(["arch=any"], ["arch=i386"], False)
200
self.assert_can_satisfy(["arch=any"], ["arch=amd64"], False)
201
self.assert_can_satisfy(["arch=any"], ["arch=arm"], False)
301
203
self.assert_can_satisfy(["cpu=64"], [], True)
302
204
self.assert_can_satisfy([], ["cpu=64"], False)
303
205
self.assert_can_satisfy(["cpu=64"], ["cpu=32"], True)
304
206
self.assert_can_satisfy(["cpu=32"], ["cpu=64"], False)
305
207
self.assert_can_satisfy(["cpu=64"], ["cpu=64"], True)
208
self.assert_can_satisfy(["cpu=0.01"], ["cpu=any"], True)
209
self.assert_can_satisfy(["cpu=9999"], ["cpu=any"], True)
210
self.assert_can_satisfy(["cpu=any"], ["cpu=any"], True)
211
self.assert_can_satisfy(["cpu=any"], ["cpu=0.01"], False)
212
self.assert_can_satisfy(["cpu=any"], ["cpu=9999"], False)
307
214
self.assert_can_satisfy(["mem=8G"], [], True)
308
215
self.assert_can_satisfy([], ["mem=8G"], False)
309
216
self.assert_can_satisfy(["mem=8G"], ["mem=4G"], True)
310
217
self.assert_can_satisfy(["mem=4G"], ["mem=8G"], False)
311
218
self.assert_can_satisfy(["mem=8G"], ["mem=8G"], True)
313
self.assert_can_satisfy(
314
# orchestra-name clears default cpu/mem values.
315
# This may be a problem.
316
["orchestra-name=henry"], [], False, "orchestra")
317
self.assert_can_satisfy(
318
[], ["orchestra-name=henry"], False, "orchestra")
319
self.assert_can_satisfy(
320
["orchestra-name=henry"], ["orchestra-name=jane"], False,
322
self.assert_can_satisfy(
323
["orchestra-name=jane"], ["orchestra-name=henry"], False,
325
self.assert_can_satisfy(
326
["orchestra-name=henry"], ["orchestra-name=henry"], True,
329
self.assert_can_satisfy(
330
["orchestra-classes=a,b"], [], True, "orchestra")
331
self.assert_can_satisfy(
332
[], ["orchestra-classes=a,b"], False, "orchestra")
333
self.assert_can_satisfy(
334
["orchestra-classes=a,b"], ["orchestra-classes=a"], True,
336
self.assert_can_satisfy(
337
["orchestra-classes=a"], ["orchestra-classes=a,b"], False,
339
self.assert_can_satisfy(
340
["orchestra-classes=a,b"], ["orchestra-classes=a,b"], True,
342
self.assert_can_satisfy(
343
["orchestra-classes=a,b"], ["orchestra-classes=a,c"], False,
346
self.assert_can_satisfy(["ec2-zone=a"], [], True, "ec2")
347
self.assert_can_satisfy([], ["ec2-zone=a"], False, "ec2")
348
self.assert_can_satisfy(["ec2-zone=a"], ["ec2-zone=b"], False, "ec2")
349
self.assert_can_satisfy(["ec2-zone=b"], ["ec2-zone=a"], False, "ec2")
350
self.assert_can_satisfy(["ec2-zone=a"], ["ec2-zone=a"], True, "ec2")
352
self.assert_can_satisfy(
353
# ec2-instance-type clears default cpu/mem values.
354
# This may be a problem.
355
["ec2-instance-type=m1.small"], [], False, "ec2")
356
self.assert_can_satisfy([], ["ec2-instance-type=m1.small"], False, "ec2")
357
self.assert_can_satisfy(
358
["ec2-instance-type=m1.large"], ["ec2-instance-type=m1.small"],
360
self.assert_can_satisfy(
361
["ec2-instance-type=m1.small"], ["ec2-instance-type=m1.large"],
363
self.assert_can_satisfy(
364
["ec2-instance-type=m1.small"], ["ec2-instance-type=m1.small"],
219
self.assert_can_satisfy(["mem=2M"], ["mem=any"], True)
220
self.assert_can_satisfy(["mem=256T"], ["mem=any"], True)
221
self.assert_can_satisfy(["mem=any"], ["mem=any"], True)
222
self.assert_can_satisfy(["mem=any"], ["mem=2M"], False)
223
self.assert_can_satisfy(["mem=any"], ["mem=256T"], False)
226
class ConstraintSetTest(TestCase):
228
def test_unregistered_name(self):
229
cs = ConstraintSet("provider")
231
e = self.assertRaises(ConstraintError, cs.parse, ["bar=2", "baz=3"])
232
self.assertEquals(str(e), "Unknown constraint: 'baz'")
234
def test_register_invisible(self):
235
cs = ConstraintSet("provider")
236
cs.register("foo", visible=False)
237
e = self.assertRaises(ConstraintError, cs.parse, ["foo=bar"])
238
self.assertEquals(str(e), "Cannot set computed constraint: 'foo'")
240
def test_register_comparer(self):
241
cs = ConstraintSet("provider")
242
cs.register("foo", comparer=operator.ne)
243
c1 = cs.parse(["foo=bar"]).with_series("series")
244
c2 = cs.parse(["foo=bar"]).with_series("series")
245
self.assertFalse(c1.can_satisfy(c2))
246
self.assertFalse(c2.can_satisfy(c1))
247
c3 = cs.parse(["foo=baz"]).with_series("series")
248
self.assertTrue(c1.can_satisfy(c3))
249
self.assertTrue(c3.can_satisfy(c1))
251
def test_register_default_and_converter(self):
252
cs = ConstraintSet("provider")
253
cs.register("foo", default="star", converter=lambda s: "death-" + s)
255
self.assertEquals(c1["foo"], "death-star")
256
c1 = cs.parse(["foo=clock"])
257
self.assertEquals(c1["foo"], "death-clock")
259
def test_convert_wraps_ValueError(self):
260
cs = ConstraintSet("provider")
261
cs.register("foo", converter=_raiser(ValueError))
262
cs.register("bar", converter=_raiser(KeyError))
263
self.assertRaises(ConstraintError, cs.parse, ["foo=1"])
264
self.assertRaises(KeyError, cs.parse, ["bar=1"])
266
def test_register_conflicts(self):
267
cs = ConstraintSet("provider")
272
cs.parse(["foo=1", "bar=2", "baz=3", "qux=4"])
274
def assert_ambiguous(strs):
275
e = self.assertRaises(ConstraintError, cs.parse, strs)
276
self.assertTrue(str(e).startswith("Ambiguous constraints"))
278
cs.register_conflicts(["foo"], ["bar", "baz", "qux"])
279
assert_ambiguous(["foo=1", "bar=2"])
280
assert_ambiguous(["foo=1", "baz=3"])
281
assert_ambiguous(["foo=1", "qux=4"])
283
cs.parse(["bar=2", "baz=3", "qux=4"])
285
cs.register_conflicts(["bar", "baz"], ["qux"])
286
assert_ambiguous(["bar=2", "qux=4"])
287
assert_ambiguous(["baz=3", "qux=4"])
289
cs.parse(["bar=2", "baz=3"])
292
def test_register_generics_no_instance_types(self):
293
cs = ConstraintSet("provider")
294
cs.register_generics([])
296
self.assertEquals(c1["arch"], "amd64")
297
self.assertEquals(c1["cpu"], 1.0)
298
self.assertEquals(c1["mem"], 512.0)
299
self.assertFalse("instance-type" in c1)
301
c2 = cs.parse(["arch=any", "cpu=0", "mem=8G"])
302
self.assertEquals(c2["arch"], None)
303
self.assertEquals(c2["cpu"], 0.0)
304
self.assertEquals(c2["mem"], 8192.0)
305
self.assertFalse("instance-type" in c2)
307
def test_register_generics_with_instance_types(self):
308
cs = ConstraintSet("provider")
309
cs.register_generics(["a1.big", "c7.peculiar"])
311
self.assertEquals(c1["arch"], "amd64")
312
self.assertEquals(c1["cpu"], 1.0)
313
self.assertEquals(c1["mem"], 512.0)
314
self.assertEquals(c1["instance-type"], None)
316
c2 = cs.parse(["arch=any", "cpu=0", "mem=8G"])
317
self.assertEquals(c2["arch"], None)
318
self.assertEquals(c2["cpu"], 0.0)
319
self.assertEquals(c2["mem"], 8192.0)
320
self.assertEquals(c2["instance-type"], None)
322
c3 = cs.parse(["instance-type=c7.peculiar", "arch=i386"])
323
self.assertEquals(c3["arch"], "i386")
324
self.assertEquals(c3["cpu"], None)
325
self.assertEquals(c3["mem"], None)
326
self.assertEquals(c3["instance-type"], "c7.peculiar")
328
def assert_ambiguous(strs):
329
e = self.assertRaises(ConstraintError, cs.parse, strs)
330
self.assertTrue(str(e).startswith("Ambiguous constraints"))
332
assert_ambiguous(["cpu=1", "instance-type=c7.peculiar"])
333
assert_ambiguous(["mem=1024", "instance-type=c7.peculiar"])
337
self.assertEquals(c4["arch"], None)
338
self.assertEquals(c4["cpu"], 0.0)
339
self.assertEquals(c4["mem"], 8192.0)
340
self.assertEquals(c4["instance-type"], None)
342
c5 = cs.parse(["instance-type=a1.big"])
343
c5.update(cs.parse(["arch=i386"]))
344
self.assertEquals(c5["arch"], "i386")
345
self.assertEquals(c5["cpu"], None)
346
self.assertEquals(c5["mem"], None)
347
self.assertEquals(c5["instance-type"], "a1.big")
349
c6 = cs.parse(["instance-type=a1.big"])
350
c6.update(cs.parse(["cpu=20"]))
351
self.assertEquals(c6["arch"], "amd64")
352
self.assertEquals(c6["cpu"], 20.0)
353
self.assertEquals(c6["mem"], None)
354
self.assertEquals(c6["instance-type"], None)
356
c7 = cs.parse(["instance-type="])
357
self.assertEquals(c7["arch"], "amd64")
358
self.assertEquals(c7["cpu"], 1.0)
359
self.assertEquals(c7["mem"], 512.0)
360
self.assertEquals(c7["instance-type"], None)
362
c8 = cs.parse(["instance-type=any"])
363
self.assertEquals(c8["arch"], "amd64")
364
self.assertEquals(c8["cpu"], 1.0)
365
self.assertEquals(c8["mem"], 512.0)
366
self.assertEquals(c8["instance-type"], None)
368
def test_load_validates_known(self):
369
cs = ConstraintSet("provider")
370
cs.register("foo", converter=_raiser(ValueError))
371
e = self.assertRaises(ConstraintError, cs.load, {"foo": "bar"})
372
self.assertEquals(str(e), "Bad 'foo' constraint 'bar': bar")
374
def test_load_preserves_unknown(self):
375
cs = ConstraintSet("provider")
376
constraints = cs.load({"foo": "bar"})
377
self.assertNotIn("foo", constraints)
378
self.assertEquals(constraints.data, {"foo": "bar"})