Skip to content

Commit def73a3

Browse files
committed
Add a bit of client side constraint validation
Fixes #661 Since the constraints are parsed at the client side, we might as well validate the constraints before sending, instead of relying on the provider's capability of handling invalid constraints. In #661 it is reported that an invalid constraint is not raising an exception on MAAS.
1 parent 7d2a423 commit def73a3

2 files changed

Lines changed: 44 additions & 15 deletions

File tree

juju/constraints.py

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,25 @@
3232
"Y": 1024 ** 6
3333
}
3434

35+
# List of supported constraint keys, see
36+
# http://github.com/cderici/juju/blob/2.9/core/constraints/constraints.go#L20-L39
37+
SUPPORTED_KEYS = [
38+
"arch",
39+
"container",
40+
"cpu_cores",
41+
"cores",
42+
"cpu_power",
43+
"mem",
44+
"root_disk",
45+
"root_disk_source",
46+
"tags",
47+
"instance_role",
48+
"instance_type",
49+
"spaces",
50+
"virt_type",
51+
"zones",
52+
"allocate_public_ip"]
53+
3554
LIST_KEYS = {'tags', 'spaces'}
3655

3756
SNAKE1 = re.compile(r'(.)([A-Z][a-z]+)')
@@ -51,24 +70,29 @@ def parse(constraints):
5170
# Fowards compatibilty: already parsed
5271
return constraints
5372

54-
constraints = {
55-
normalize_key(k): (
56-
normalize_list_value(v) if k in LIST_KEYS else
57-
normalize_value(v)
58-
) for k, v in [s.split("=") for s in constraints.split(" ")]}
73+
normalized_constraints = {}
74+
for s in constraints.split(" "):
75+
if "=" not in s:
76+
raise Exception("malformed constraint %s" % s)
77+
78+
k, v = s.split("=")
79+
normalized_constraints[normalize_key(k)] = normalize_list_value(v) if\
80+
k in LIST_KEYS else normalize_value(v)
5981

60-
return constraints
82+
return normalized_constraints
6183

6284

63-
def normalize_key(key):
64-
key = key.strip()
85+
def normalize_key(orig_key):
86+
key = orig_key.strip()
6587

6688
key = key.replace("-", "_") # Our _client lib wants "_" in place of "-"
6789

6890
# Convert camelCase to snake_case
6991
key = SNAKE1.sub(r'\1_\2', key)
7092
key = SNAKE2.sub(r'\1_\2', key).lower()
7193

94+
if key not in SUPPORTED_KEYS:
95+
raise Exception("unknown constraint in %s" % orig_key)
7296
return key
7397

7498

tests/unit/test_constraints.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ def test_mem_regex(self):
2020
def test_normalize_key(self):
2121
_ = constraints.normalize_key
2222

23-
self.assertEqual(_("test-key"), "test_key")
24-
self.assertEqual(_("test-key "), "test_key")
25-
self.assertEqual(_(" test-key"), "test_key")
26-
self.assertEqual(_("TestKey"), "test_key")
27-
self.assertEqual(_("testKey"), "test_key")
23+
self.assertEqual(_("root-disk"), "root_disk")
24+
self.assertEqual(_("root-disk "), "root_disk")
25+
self.assertEqual(_(" root-disk"), "root_disk")
26+
self.assertEqual(_("RootDisk"), "root_disk")
27+
self.assertEqual(_("rootDisk"), "root_disk")
28+
29+
self.assertRaises(Exception, lambda: _("not-one-of-the-supported-keys"))
2830

2931
def test_normalize_val(self):
3032
_ = constraints.normalize_value
@@ -53,13 +55,16 @@ def test_parse_constraints(self):
5355
)
5456

5557
self.assertEqual(
56-
_("mem=10G foo=bar,baz tags=tag1 spaces=space1,space2"),
58+
_("mem=10G zones=bar,baz tags=tag1 spaces=space1,space2"),
5759
{"mem": 10 * 1024,
58-
"foo": "bar,baz",
60+
"zones": "bar,baz",
5961
"tags": ["tag1"],
6062
"spaces": ["space1", "space2"]}
6163
)
6264

65+
self.assertRaises(Exception, lambda: _("root-disk>16G"))
66+
self.assertRaises(Exception, lambda: _("root-disk>=16G"))
67+
6368
def test_parse_storage_constraint(self):
6469
_ = constraints.parse_storage_constraint
6570

0 commit comments

Comments
 (0)