Merge ~cgrabowski/maas:reverse_unsubscribe_logic into maas:master
- Git
- lp:~cgrabowski/maas
- reverse_unsubscribe_logic
- Merge into master
Status: | Merged |
---|---|
Approved by: | Christian Grabowski |
Approved revision: | 7cd89f4e08129cb5519836200e7d325a6f6cc7cf |
Merge reported by: | MAAS Lander |
Merged at revision: | not available |
Proposed branch: | ~cgrabowski/maas:reverse_unsubscribe_logic |
Merge into: | maas:master |
Diff against target: |
274 lines (+95/-92) 5 files modified
src/maasserver/websockets/base.py (+11/-21) src/maasserver/websockets/handlers/machine.py (+12/-0) src/maasserver/websockets/handlers/tests/test_machine.py (+71/-0) src/maasserver/websockets/tests/test_base.py (+0/-50) src/maasserver/websockets/tests/test_protocol.py (+1/-21) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alexsander de Souza | Approve | ||
MAAS Lander | Approve | ||
Review via email: mp+426761@code.launchpad.net |
Commit message
add back a set of unsubscribed pks to know when to ignore an update
update units to reflect allow-list behavior
reverse unsubscribe endpoint logic to receive an allow-list
Description of the change
- c36b1b1... by Christian Grabowski
-
remove no longer relevant test
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b reverse_
STATUS: FAILED
LOG: http://
COMMIT: 27d6b8cf2b0a378
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b reverse_
STATUS: SUCCESS
COMMIT: c36b1b185951f71
- 02a30d6... by Christian Grabowski
-
override machine's listen function to only listen for machines in loaded_pks
- 7cd89f4... by Christian Grabowski
-
safely access active_pk
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b reverse_
STATUS: SUCCESS
COMMIT: a704ef6445d66fd
Alexsander de Souza (alexsander-souza) : | # |
Christian Grabowski (cgrabowski) : | # |
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b reverse_
STATUS: SUCCESS
COMMIT: 7cd89f4e08129cb
Alexsander de Souza (alexsander-souza) wrote : | # |
+1
Preview Diff
1 | diff --git a/src/maasserver/websockets/base.py b/src/maasserver/websockets/base.py | |||
2 | index 3063777..8787174 100644 | |||
3 | --- a/src/maasserver/websockets/base.py | |||
4 | +++ b/src/maasserver/websockets/base.py | |||
5 | @@ -103,7 +103,6 @@ class HandlerOptions: | |||
6 | 103 | pk = "id" | 103 | pk = "id" |
7 | 104 | bulk_pk = "ids" | 104 | bulk_pk = "ids" |
8 | 105 | pk_type = int | 105 | pk_type = int |
9 | 106 | unsubscribed_pks = set() | ||
10 | 107 | fields = None | 106 | fields = None |
11 | 108 | exclude = None | 107 | exclude = None |
12 | 109 | list_fields = None | 108 | list_fields = None |
13 | @@ -340,7 +339,6 @@ class Handler(metaclass=HandlerMetaclass): | |||
14 | 340 | permission = self._meta.view_permission | 339 | permission = self._meta.view_permission |
15 | 341 | if not self.user.has_perm(permission, obj): | 340 | if not self.user.has_perm(permission, obj): |
16 | 342 | raise HandlerPermissionError() | 341 | raise HandlerPermissionError() |
17 | 343 | self._meta.unsubscribed_pks.discard(pk) | ||
18 | 344 | return obj | 342 | return obj |
19 | 345 | 343 | ||
20 | 346 | def get_queryset(self, for_list=False): | 344 | def get_queryset(self, for_list=False): |
21 | @@ -465,7 +463,6 @@ class Handler(metaclass=HandlerMetaclass): | |||
22 | 465 | """Cache all loaded object pks.""" | 463 | """Cache all loaded object pks.""" |
23 | 466 | getpk = attrgetter(self._meta.pk) | 464 | getpk = attrgetter(self._meta.pk) |
24 | 467 | self.cache["loaded_pks"].update(getpk(obj) for obj in objs) | 465 | self.cache["loaded_pks"].update(getpk(obj) for obj in objs) |
25 | 468 | [self._meta.unsubscribed_pks.discard(getpk(obj)) for obj in objs] | ||
26 | 469 | 466 | ||
27 | 470 | def _filter(self, qs, action, params): | 467 | def _filter(self, qs, action, params): |
28 | 471 | """Return a filtered queryset | 468 | """Return a filtered queryset |
29 | @@ -812,11 +809,7 @@ class Handler(metaclass=HandlerMetaclass): | |||
30 | 812 | :param action: Action that caused this event. | 809 | :param action: Action that caused this event. |
31 | 813 | :param pk: Id of the object. | 810 | :param pk: Id of the object. |
32 | 814 | """ | 811 | """ |
38 | 815 | return ( | 812 | return self.get_object({self._meta.pk: pk}) |
34 | 816 | self.get_object({self._meta.pk: pk}) | ||
35 | 817 | if pk not in self._meta.unsubscribed_pks | ||
36 | 818 | else None | ||
37 | 819 | ) | ||
39 | 820 | 813 | ||
40 | 821 | def refetch(self, obj): | 814 | def refetch(self, obj): |
41 | 822 | """Refetch an object using the handler queryset. | 815 | """Refetch an object using the handler queryset. |
42 | @@ -826,22 +819,19 @@ class Handler(metaclass=HandlerMetaclass): | |||
43 | 826 | """ | 819 | """ |
44 | 827 | return self.get_object({self._meta.pk: getattr(obj, self._meta.pk)}) | 820 | return self.get_object({self._meta.pk: getattr(obj, self._meta.pk)}) |
45 | 828 | 821 | ||
46 | 829 | def _unsubscribe(self, pk): | ||
47 | 830 | if pk == self.cache.get("active_pk"): | ||
48 | 831 | del self.cache["active_pk"] | ||
49 | 832 | |||
50 | 833 | if pk in self.cache.get("loaded_pks", []): | ||
51 | 834 | self.cache["loaded_pks"].remove(pk) | ||
52 | 835 | |||
53 | 836 | self._meta.unsubscribed_pks.add(pk) | ||
54 | 837 | |||
55 | 838 | return pk | ||
56 | 839 | |||
57 | 840 | def unsubscribe(self, params): | 822 | def unsubscribe(self, params): |
58 | 841 | if self._meta.pk in params: | 823 | if self._meta.pk in params: |
60 | 842 | return self._unsubscribe(params[self._meta.pk]) | 824 | pk = params[self._meta.pk] |
61 | 825 | if pk == self.cache.get("active_pk"): | ||
62 | 826 | del self.cache["active_pk"] | ||
63 | 827 | self.cache["loaded_pks"] = self.cache["loaded_pks"] - set(pk) | ||
64 | 828 | return [pk] | ||
65 | 843 | elif self._meta.bulk_pk in params: | 829 | elif self._meta.bulk_pk in params: |
67 | 844 | return [self._unsubscribe(pk) for pk in params[self._meta.bulk_pk]] | 830 | pks = set(params[self._meta.bulk_pk]) |
68 | 831 | if self.cache.get("active_pk") in pks: | ||
69 | 832 | del self.cache["active_pk"] | ||
70 | 833 | self.cache["loaded_pks"] = self.cache["loaded_pks"] - pks | ||
71 | 834 | return pks | ||
72 | 845 | else: | 835 | else: |
73 | 846 | raise HandlerValidationError( | 836 | raise HandlerValidationError( |
74 | 847 | f"'{self._meta.pk}' or '{self._meta.bulk_pk}' must be provided in params for unsubscribe" | 837 | f"'{self._meta.pk}' or '{self._meta.bulk_pk}' must be provided in params for unsubscribe" |
75 | diff --git a/src/maasserver/websockets/handlers/machine.py b/src/maasserver/websockets/handlers/machine.py | |||
76 | index 93cd2eb..955c350 100644 | |||
77 | --- a/src/maasserver/websockets/handlers/machine.py | |||
78 | +++ b/src/maasserver/websockets/handlers/machine.py | |||
79 | @@ -521,6 +521,18 @@ class MachineHandler(NodeHandler): | |||
80 | 521 | % (op, " or ".join(status_names)) | 521 | % (op, " or ".join(status_names)) |
81 | 522 | ) | 522 | ) |
82 | 523 | 523 | ||
83 | 524 | def listen(self, channel, action, pk): | ||
84 | 525 | """Called when the handler listens for events on channels with | ||
85 | 526 | `Meta.listen_channels`. | ||
86 | 527 | |||
87 | 528 | :param channel: Channel event occured on. | ||
88 | 529 | :param action: Action that caused this event. | ||
89 | 530 | :param pk: Id of the object. | ||
90 | 531 | """ | ||
91 | 532 | # if loaded / not unsubscrived, allow listen events | ||
92 | 533 | if pk in self.cache["loaded_pks"] or pk == self.cache.get("active_pk"): | ||
93 | 534 | return self.get_object({self._meta.pk: pk}) | ||
94 | 535 | |||
95 | 524 | def update_filesystem(self, params): | 536 | def update_filesystem(self, params): |
96 | 525 | node = self._get_node_or_permission_error( | 537 | node = self._get_node_or_permission_error( |
97 | 526 | params, permission=NodePermission.edit | 538 | params, permission=NodePermission.edit |
98 | diff --git a/src/maasserver/websockets/handlers/tests/test_machine.py b/src/maasserver/websockets/handlers/tests/test_machine.py | |||
99 | index 352d4ea..c4d3c6f 100644 | |||
100 | --- a/src/maasserver/websockets/handlers/tests/test_machine.py | |||
101 | +++ b/src/maasserver/websockets/handlers/tests/test_machine.py | |||
102 | @@ -6271,3 +6271,74 @@ class TestMachineHandlerFilter(MAASServerTestCase): | |||
103 | 6271 | _assert_value_in(machine.bmc.power_type, "not_pod_type") | 6271 | _assert_value_in(machine.bmc.power_type, "not_pod_type") |
104 | 6272 | _assert_value_in(machine.bmc.id, "pod") | 6272 | _assert_value_in(machine.bmc.id, "pod") |
105 | 6273 | _assert_value_in(machine.bmc.id, "not_pod") | 6273 | _assert_value_in(machine.bmc.id, "not_pod") |
106 | 6274 | |||
107 | 6275 | def test_unsubscribe_prevents_further_updates_for_pk(self): | ||
108 | 6276 | admin = factory.make_admin() | ||
109 | 6277 | handler = MachineHandler(admin, {}, None) | ||
110 | 6278 | node = factory.make_Node() | ||
111 | 6279 | handler.list({}) | ||
112 | 6280 | listen_result = handler.on_listen("machine", "update", node.system_id) | ||
113 | 6281 | self.assertIsNotNone(listen_result) | ||
114 | 6282 | handler.unsubscribe({"system_ids": [node.system_id]}) | ||
115 | 6283 | self.assertIsNone( | ||
116 | 6284 | handler.on_listen("machine", "update", node.system_id) | ||
117 | 6285 | ) | ||
118 | 6286 | list_result = handler.list({}) | ||
119 | 6287 | self.assertEqual(len(list_result), 1) | ||
120 | 6288 | |||
121 | 6289 | def test_unsubscribe_raises_validation_error_with_no_pk(self): | ||
122 | 6290 | admin = factory.make_admin() | ||
123 | 6291 | handler = MachineHandler(admin, {}, None) | ||
124 | 6292 | self.assertRaises(HandlerValidationError, handler.unsubscribe, {}) | ||
125 | 6293 | |||
126 | 6294 | def test_read_an_unsubscribed_object_subscribes(self): | ||
127 | 6295 | admin = factory.make_admin() | ||
128 | 6296 | handler = MachineHandler(admin, {}, None) | ||
129 | 6297 | node1 = factory.make_Node() | ||
130 | 6298 | node2 = factory.make_Node() | ||
131 | 6299 | handler.list({}) | ||
132 | 6300 | self.assertIsNotNone( | ||
133 | 6301 | handler.on_listen("machine", "update", node1.system_id) | ||
134 | 6302 | ) | ||
135 | 6303 | self.assertIsNotNone( | ||
136 | 6304 | handler.on_listen("machine", "update", node2.system_id) | ||
137 | 6305 | ) | ||
138 | 6306 | handler.unsubscribe({"system_ids": [node2.system_id]}) | ||
139 | 6307 | self.assertIsNotNone( | ||
140 | 6308 | handler.on_listen("machine", "update", node1.system_id) | ||
141 | 6309 | ) | ||
142 | 6310 | self.assertIsNone( | ||
143 | 6311 | handler.on_listen("machine", "update", node2.system_id) | ||
144 | 6312 | ) | ||
145 | 6313 | self.assertIsNotNone(handler.get({"system_id": node2.system_id})) | ||
146 | 6314 | self.assertIsNotNone( | ||
147 | 6315 | handler.on_listen("machine", "update", node2.system_id) | ||
148 | 6316 | ) | ||
149 | 6317 | |||
150 | 6318 | def test_list_an_unsubscribed_object_subscribes(self): | ||
151 | 6319 | admin = factory.make_admin() | ||
152 | 6320 | handler = MachineHandler(admin, {}, None) | ||
153 | 6321 | node1 = factory.make_Node() | ||
154 | 6322 | node2 = factory.make_Node() | ||
155 | 6323 | handler.list({}) | ||
156 | 6324 | self.assertIsNotNone( | ||
157 | 6325 | handler.on_listen("machine", "update", node1.system_id) | ||
158 | 6326 | ) | ||
159 | 6327 | self.assertIsNotNone( | ||
160 | 6328 | handler.on_listen("machine", "update", node2.system_id) | ||
161 | 6329 | ) | ||
162 | 6330 | handler.unsubscribe({"system_ids": [node1.system_id]}) | ||
163 | 6331 | self.assertIsNotNone( | ||
164 | 6332 | handler.on_listen("machine", "update", node2.system_id) | ||
165 | 6333 | ) | ||
166 | 6334 | self.assertIsNone( | ||
167 | 6335 | handler.on_listen("machine", "update", node1.system_id) | ||
168 | 6336 | ) | ||
169 | 6337 | list_result = handler.list({}) | ||
170 | 6338 | self.assertEqual(len(list_result), 2) | ||
171 | 6339 | self.assertIsNotNone( | ||
172 | 6340 | handler.on_listen("machine", "update", node1.system_id) | ||
173 | 6341 | ) | ||
174 | 6342 | self.assertIsNotNone( | ||
175 | 6343 | handler.on_listen("machine", "update", node2.system_id) | ||
176 | 6344 | ) | ||
177 | diff --git a/src/maasserver/websockets/tests/test_base.py b/src/maasserver/websockets/tests/test_base.py | |||
178 | index 77f3fd3..e7632b1 100644 | |||
179 | --- a/src/maasserver/websockets/tests/test_base.py | |||
180 | +++ b/src/maasserver/websockets/tests/test_base.py | |||
181 | @@ -986,56 +986,6 @@ class TestHandler(MAASServerTestCase, FakeNodesHandlerMixin): | |||
182 | 986 | for idx in range(3): | 986 | for idx in range(3): |
183 | 987 | self.assertEqual(f"host-{2-idx}", result[idx]["hostname"]) | 987 | self.assertEqual(f"host-{2-idx}", result[idx]["hostname"]) |
184 | 988 | 988 | ||
185 | 989 | def test_unsubscribe_prevents_further_updates_for_pk(self): | ||
186 | 990 | handler = self.make_nodes_handler() | ||
187 | 991 | node = factory.make_Node() | ||
188 | 992 | handler._meta.queryset = Node.objects.all() | ||
189 | 993 | handler._meta.listen_channels = ["node"] | ||
190 | 994 | handler._meta.bulk_pk = "system_ids" | ||
191 | 995 | handler._meta.pk = "system_id" | ||
192 | 996 | listen_result = handler.listen("node", "update", node.system_id) | ||
193 | 997 | self.assertIsNotNone(listen_result) | ||
194 | 998 | handler.unsubscribe({"system_ids": [node.system_id]}) | ||
195 | 999 | self.assertIsNone(handler.listen("node", "update", node.system_id)) | ||
196 | 1000 | list_result = handler.list({}) | ||
197 | 1001 | self.assertEqual(len(list_result), 1) | ||
198 | 1002 | |||
199 | 1003 | def test_unsubscribe_raises_validation_error_with_no_pk(self): | ||
200 | 1004 | handler = self.make_nodes_handler() | ||
201 | 1005 | handler._meta.queryset = Node.objects.all() | ||
202 | 1006 | handler._meta.listen_channels = ["node"] | ||
203 | 1007 | handler._meta.bulk_pk = "system_ids" | ||
204 | 1008 | handler._meta.pk = "system_id" | ||
205 | 1009 | self.assertRaises(HandlerValidationError, handler.unsubscribe, {}) | ||
206 | 1010 | |||
207 | 1011 | def test_read_an_unsubscribed_object_subscribes(self): | ||
208 | 1012 | handler = self.make_nodes_handler() | ||
209 | 1013 | node = factory.make_Node() | ||
210 | 1014 | handler._meta.queryset = Node.objects.all() | ||
211 | 1015 | handler._meta.listen_channels = ["node"] | ||
212 | 1016 | handler._meta.bulk_pk = "system_ids" | ||
213 | 1017 | handler._meta.pk = "system_id" | ||
214 | 1018 | self.assertIsNotNone(handler.listen("node", "update", node.system_id)) | ||
215 | 1019 | handler.unsubscribe({"system_ids": [node.system_id]}) | ||
216 | 1020 | self.assertIsNone(handler.listen("node", "update", node.system_id)) | ||
217 | 1021 | self.assertIsNotNone(handler.get({"system_id": node.system_id})) | ||
218 | 1022 | self.assertIsNotNone(handler.listen("node", "update", node.system_id)) | ||
219 | 1023 | |||
220 | 1024 | def test_list_an_unsubscribed_object_subscribes(self): | ||
221 | 1025 | handler = self.make_nodes_handler() | ||
222 | 1026 | node = factory.make_Node() | ||
223 | 1027 | handler._meta.queryset = Node.objects.all() | ||
224 | 1028 | handler._meta.listen_channels = ["node"] | ||
225 | 1029 | handler._meta.bulk_pk = "system_ids" | ||
226 | 1030 | handler._meta.pk = "system_id" | ||
227 | 1031 | listen_result = handler.listen("node", "update", node.system_id) | ||
228 | 1032 | self.assertIsNotNone(listen_result) | ||
229 | 1033 | handler.unsubscribe({"system_ids": [node.system_id]}) | ||
230 | 1034 | self.assertIsNone(handler.listen("node", "update", node.system_id)) | ||
231 | 1035 | list_result = handler.list({}) | ||
232 | 1036 | self.assertEqual(len(list_result), 1) | ||
233 | 1037 | self.assertIsNotNone(handler.listen("node", "update", node.system_id)) | ||
234 | 1038 | |||
235 | 1039 | 989 | ||
236 | 1040 | class TestHandlerGrouping(MAASServerTestCase, FakeNodesHandlerMixin): | 990 | class TestHandlerGrouping(MAASServerTestCase, FakeNodesHandlerMixin): |
237 | 1041 | def test_group_simple(self): | 991 | def test_group_simple(self): |
238 | diff --git a/src/maasserver/websockets/tests/test_protocol.py b/src/maasserver/websockets/tests/test_protocol.py | |||
239 | index b5cbdc7..5181adb 100644 | |||
240 | --- a/src/maasserver/websockets/tests/test_protocol.py | |||
241 | +++ b/src/maasserver/websockets/tests/test_protocol.py | |||
242 | @@ -20,10 +20,7 @@ from apiclient.utils import ascii_url | |||
243 | 20 | from maasserver.eventloop import services | 20 | from maasserver.eventloop import services |
244 | 21 | from maasserver.testing.factory import factory as maas_factory | 21 | from maasserver.testing.factory import factory as maas_factory |
245 | 22 | from maasserver.testing.listener import FakePostgresListenerService | 22 | from maasserver.testing.listener import FakePostgresListenerService |
250 | 23 | from maasserver.testing.testcase import ( | 23 | from maasserver.testing.testcase import MAASTransactionServerTestCase |
247 | 24 | MAASServerTestCase, | ||
248 | 25 | MAASTransactionServerTestCase, | ||
249 | 26 | ) | ||
251 | 27 | from maasserver.utils.orm import transactional | 24 | from maasserver.utils.orm import transactional |
252 | 28 | from maasserver.utils.threads import deferToDatabase | 25 | from maasserver.utils.threads import deferToDatabase |
253 | 29 | from maasserver.websockets import protocol as protocol_module | 26 | from maasserver.websockets import protocol as protocol_module |
254 | @@ -1003,20 +1000,3 @@ class TestWebSocketFactoryTransactional( | |||
255 | 1003 | controller.system_id, | 1000 | controller.system_id, |
256 | 1004 | ), | 1001 | ), |
257 | 1005 | ) | 1002 | ) |
258 | 1006 | |||
259 | 1007 | |||
260 | 1008 | class TestWebSocketFactoryServer(MAASServerTestCase, MakeProtocolFactoryMixin): | ||
261 | 1009 | def test_processNotify_unsubscribed_object(self): | ||
262 | 1010 | factory = self.make_factory() | ||
263 | 1011 | machine = maas_factory.make_Machine() | ||
264 | 1012 | handler = MachineHandler(maas_factory.make_User(), {}, None) | ||
265 | 1013 | factory.handlers["machine"] = handler | ||
266 | 1014 | result = factory.processNotify( | ||
267 | 1015 | handler, "machine", "update", machine.system_id | ||
268 | 1016 | ) | ||
269 | 1017 | self.assertIsNotNone(result) | ||
270 | 1018 | handler.unsubscribe({"system_ids": [machine.system_id]}) | ||
271 | 1019 | result = factory.processNotify( | ||
272 | 1020 | handler, "machine", "update", machine.system_id | ||
273 | 1021 | ) | ||
274 | 1022 | self.assertIsNone(result) |
UNIT TESTS unsubscribe_ logic lp:~cgrabowski/maas/+git/maas into -b master lp:~maas-committers/maas
-b reverse_
STATUS: FAILED maas-ci. internal: 8080/job/ maas-tester/ 34/consoleText 86d8032e9290965 7784f22bd4
LOG: http://
COMMIT: 93893ed47c2552b