Skip to content

Commit 44065c8

Browse files
committed
Don't insert previously negated items in Applications.append_if_new()
We add logic in `Applications.append_if_new()` to only insert non-negated items if they aren't already on our internal negation list. Additionally, we drop items from the negation list, once we've applied the negation once either in `append_if_new()` or in `merge_unique()` so that patterns like adding, then removing and then adding an item again have the expected result of the item being present in the final list. This fixes the issue where it wasn't possible to preemptively remove an entry from the applications list in a multi-dimensional hierarchy, e.g. in a [Commodore] global defaults repository where we may want to exclude applications for a certain Kubernetes distribution regardless of the cloud on which a cluster with that distribution is running. [Commodore]: https://syn.tools/commodore
1 parent 80d6d15 commit 44065c8

File tree

10 files changed

+79
-3
lines changed

10 files changed

+79
-3
lines changed

reclass/datatypes/applications.py

+24-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,21 @@ class Applications(Classes):
2323
a reference of the negation is kept, in case the instance is later used to
2424
extend another instance, in which case the negations should apply to the
2525
instance to be extended.
26+
27+
Negations are dropped after they've been applied once, so that patterns like
28+
29+
```
30+
applications:
31+
- item
32+
33+
applications:
34+
- ~item
35+
36+
applications:
37+
- item
38+
```
39+
40+
result in `item` being present in the final list.
2641
'''
2742
DEFAULT_NEGATION_PREFIX = '~'
2843

@@ -37,12 +52,17 @@ def append_if_new(self, item):
3752
self._assert_is_string(item)
3853
if item.startswith(self.negation_prefix):
3954
item = item[self._offset:]
40-
self._negations.append(item)
4155
try:
4256
self._items.remove(item)
4357
except ValueError:
44-
pass
58+
# Only keep the negation if we couldn't remove the indicated item.
59+
self._negations.append(item)
60+
elif item in self._negations:
61+
# Remove previously negated items from the negations list instead of
62+
# inserting them into the list.
63+
self._negations.remove(item)
4564
else:
65+
# Insert non-negated items if they're not in our negations list already.
4666
super(Applications, self)._append_if_new(item)
4767

4868
def merge_unique(self, iterable):
@@ -53,8 +73,9 @@ def merge_unique(self, iterable):
5373
try:
5474
self._items.remove(negation)
5575
except ValueError:
76+
# only remember negations which we didn't process during the merge
77+
self._negations.append(negation)
5678
pass
57-
self._negations.extend(iterable._negations)
5879
iterable = iterable.as_list()
5980
for i in iterable:
6081
self.append_if_new(i)
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
applications:
2+
- ~foo
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
applications:
2+
- foo
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
classes:
2+
- cls2
3+
- cls1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
applications:
2+
- foo

reclass/tests/data/07/nodes/A.yml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
classes:
2+
- cls1
3+
- cls2
4+
5+
applications:
6+
- foo
7+
8+
parameters:
9+
expected_apps:
10+
- foo

reclass/tests/data/07/nodes/B.yml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
classes:
2+
- cls2
3+
- cls1
4+
5+
parameters:
6+
expected_apps: []

reclass/tests/data/07/nodes/C.yml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
classes:
2+
- cls2
3+
- cls1
4+
5+
applications:
6+
- foo
7+
8+
parameters:
9+
expected_apps:
10+
- foo

reclass/tests/data/07/nodes/D.yml

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
classes:
2+
- global
3+
- override
4+
5+
parameters:
6+
expected_apps:
7+
- foo

reclass/tests/test_core.py

+13
Original file line numberDiff line numberDiff line change
@@ -123,5 +123,18 @@ def test_application_removal(self):
123123
self.assertEqual(A_node['applications'], A_node['parameters']['expected_apps'])
124124
self.assertEqual(B_node['applications'], B_node['parameters']['expected_apps'])
125125

126+
def test_application_removal_before_insertion(self):
127+
reclass = self._core('07')
128+
129+
A_node = reclass.nodeinfo('A')
130+
B_node = reclass.nodeinfo('B')
131+
C_node = reclass.nodeinfo('C')
132+
D_node = reclass.nodeinfo('D')
133+
134+
self.assertEqual(A_node['applications'], A_node['parameters']['expected_apps'])
135+
self.assertEqual(B_node['applications'], B_node['parameters']['expected_apps'])
136+
self.assertEqual(C_node['applications'], C_node['parameters']['expected_apps'])
137+
self.assertEqual(D_node['applications'], D_node['parameters']['expected_apps'])
138+
126139
if __name__ == '__main__':
127140
unittest.main()

0 commit comments

Comments
 (0)