Skip to content

Commit 276ecf2

Browse files
authored
Merge pull request #666 from cderici/improve-constraint-parsing
#666 #### Description 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. Fixes #661 #### QA Steps ```sh tox -e py3 -- tests/unit/test_constraints.py ``` or you should see an error when you try to use an invalid constraint (e.g. `root-disk>=16G`) ```sh Python 3.9.7 (default, Sep 10 2021, 14:59:43) [GCC 11.2.0] on linux >>> import juju >>> import juju.constraints >>> juju.constraints.parse('tags=scalebot-dev-controller arch=amd64 root-disk>=16G') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/caner/work/cderici/python-libjuju/juju/constraints.py", line 79, in parse normalized_constraints[normalize_key(k)] = normalize_list_value(v) if\n File "/home/caner/work/cderici/python-libjuju/juju/constraints.py", line 95, in normalize_key raise Exception("unknown constraint in %s" % orig_key) Exception: unknown constraint in root-disk> >>> ``` #### Notes & Discussion This is fundamentally not the ideal way of handling the constraints, which is also related to https://bugs.launchpad.net/juju/+bug/1645402, however, as long as the clients are responsible for parsing the constraints, it makes sense to avoid any unnecessary api calls due to invalid constraints.
2 parents 7d2a423 + def73a3 commit 276ecf2

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)