Merge ~ltrager/maas:lp1870171_2.3 into maas:2.3
- Git
- lp:~ltrager/maas
- lp1870171_2.3
- Merge into 2.3
Proposed by
Lee Trager
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Lee Trager | ||||
Approved revision: | c95e561d3333430526370220a3c340d3ce34fd42 | ||||
Merge reported by: | MAAS Lander | ||||
Merged at revision: | not available | ||||
Proposed branch: | ~ltrager/maas:lp1870171_2.3 | ||||
Merge into: | maas:2.3 | ||||
Diff against target: |
564 lines (+153/-120) 10 files modified
src/maasserver/api/account.py (+1/-1) src/maasserver/api/machines.py (+21/-9) src/maasserver/api/tests/test_api.py (+15/-2) src/maasserver/api/tests/test_machine.py (+40/-16) src/maasserver/api/tests/test_machines.py (+13/-48) src/maasserver/models/node.py (+10/-7) src/maasserver/models/tests/test_node.py (+29/-32) src/maasserver/models/tests/test_userprofile.py (+9/-1) src/maasserver/models/userprofile.py (+7/-2) src/maasserver/node_action.py (+8/-2) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Lee Trager (community) | Approve | ||
Review via email: mp+382099@code.launchpad.net |
Commit message
Backport of d0653c9 LP: #1870171 - Remove the token column from the Node table.
The token column is only used by the API when allocating and machine and
listing allocated machines. When a User Token is deleted this association
causes the Node to be deleted as well. Allocated machines are already
filtered by owner and status so the token column isn't needed.
In the backport Node.token is no longer used but the column remains to
avoid creating a migration.
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/src/maasserver/api/account.py b/src/maasserver/api/account.py |
2 | index adc8722..ba6ed54 100644 |
3 | --- a/src/maasserver/api/account.py |
4 | +++ b/src/maasserver/api/account.py |
5 | @@ -1,4 +1,4 @@ |
6 | -# Copyright 2014-2016 Canonical Ltd. This software is licensed under the |
7 | +# Copyright 2014-2020 Canonical Ltd. This software is licensed under the |
8 | # GNU Affero General Public License version 3 (see the file LICENSE). |
9 | |
10 | """API handler: `Account`.""" |
11 | diff --git a/src/maasserver/api/machines.py b/src/maasserver/api/machines.py |
12 | index e8b671b..9482631 100644 |
13 | --- a/src/maasserver/api/machines.py |
14 | +++ b/src/maasserver/api/machines.py |
15 | @@ -1,4 +1,4 @@ |
16 | -# Copyright 2015-2017 Canonical Ltd. This software is licensed under the |
17 | +# Copyright 2015-2020 Canonical Ltd. This software is licensed under the |
18 | # GNU Affero General Public License version 3 (see the file LICENSE). |
19 | |
20 | __all__ = [ |
21 | @@ -41,7 +41,6 @@ from maasserver.api.support import ( |
22 | ) |
23 | from maasserver.api.utils import ( |
24 | get_mandatory_param, |
25 | - get_oauth_token, |
26 | get_optional_list, |
27 | get_optional_param, |
28 | ) |
29 | @@ -525,7 +524,7 @@ class MachineHandler(NodeHandler, OwnerDataMixin, PowerMixin): |
30 | "Request from user %s to acquire machine: %s (%s)", |
31 | request.user.username, machine.fqdn, machine.system_id) |
32 | machine.acquire( |
33 | - request.user, get_oauth_token(request), |
34 | + request.user, |
35 | agent_name=agent_name, comment=comment, |
36 | bridge_all=bridge_all, bridge_stp=bridge_stp, |
37 | bridge_fd=bridge_fd) |
38 | @@ -1314,11 +1313,24 @@ class MachinesHandler(NodesHandler, PowersMixin): |
39 | |
40 | @operation(idempotent=True) |
41 | def list_allocated(self, request): |
42 | - """Fetch Machines that were allocated to the User/oauth token.""" |
43 | - token = get_oauth_token(request) |
44 | - match_ids = get_optional_list(request.GET, 'id') |
45 | - machines = Machine.objects.get_allocated_visible_machines( |
46 | - token, match_ids) |
47 | + """@description-title List allocated |
48 | + @description List machines that were allocated to the User. |
49 | + |
50 | + @success (http-status-code) "200" 200 |
51 | + @success (json) "success-json" A JSON object containing a list of |
52 | + allocated machines. |
53 | + @success-example "success-json" [exkey=machines-placeholder] |
54 | + placeholder text |
55 | + """ |
56 | + # limit to machines that the user can view |
57 | + machines = Machine.objects.get_nodes( |
58 | + request.user, NODE_PERMISSION.VIEW) |
59 | + machines = machines.filter( |
60 | + owner=request.user, status=NODE_STATUS.ALLOCATED |
61 | + ) |
62 | + system_ids = get_optional_list(request.GET, "id") |
63 | + if system_ids: |
64 | + machines = machines.filter(system_id__in=system_ids) |
65 | return machines.order_by('id') |
66 | |
67 | @operation(idempotent=False) |
68 | @@ -1606,7 +1618,7 @@ class MachinesHandler(NodesHandler, PowersMixin): |
69 | raise NodesNotAvailable(message) |
70 | if not dry_run: |
71 | machine.acquire( |
72 | - request.user, get_oauth_token(request), |
73 | + request.user, |
74 | agent_name=agent_name, comment=comment, |
75 | bridge_all=bridge_all, bridge_stp=bridge_stp, |
76 | bridge_fd=bridge_fd) |
77 | diff --git a/src/maasserver/api/tests/test_api.py b/src/maasserver/api/tests/test_api.py |
78 | index 08892d9..4e8b9e0 100644 |
79 | --- a/src/maasserver/api/tests/test_api.py |
80 | +++ b/src/maasserver/api/tests/test_api.py |
81 | @@ -1,4 +1,4 @@ |
82 | -# Copyright 2012-2016 Canonical Ltd. This software is licensed under the |
83 | +# Copyright 2012-2020 Canonical Ltd. This software is licensed under the |
84 | # GNU Affero General Public License version 3 (see the file LICENSE). |
85 | |
86 | """Test maasserver API.""" |
87 | @@ -45,7 +45,10 @@ from maasserver.testing.testclient import MAASSensibleOAuthClient |
88 | from maasserver.utils.converters import json_load_bytes |
89 | from maasserver.utils.django_urls import reverse |
90 | from maasserver.utils.keys import ImportSSHKeysError |
91 | -from maasserver.utils.orm import get_one |
92 | +from maasserver.utils.orm import ( |
93 | + get_one, |
94 | + reload_object, |
95 | +) |
96 | from maastesting.matchers import MockCalledOnceWith |
97 | from maastesting.testcase import MAASTestCase |
98 | from piston3.doc import generate_doc |
99 | @@ -288,6 +291,16 @@ class AccountAPITest(APITestCase.ForUser): |
100 | |
101 | self.assertEqual(http.client.BAD_REQUEST, response.status_code) |
102 | |
103 | + def test_delete_authorisation_token_doesnt_delete_node(self): |
104 | + token = self.user.tokens.first() |
105 | + node = factory.make_Node(owner=self.user, token=token) |
106 | + response = self.client.post( |
107 | + reverse("account_handler"), |
108 | + {"op": "delete_authorisation_token", "token_key": token.key}, |
109 | + ) |
110 | + self.assertEqual(http.client.NO_CONTENT, response.status_code) |
111 | + self.assertIsNotNone(reload_object(node)) |
112 | + |
113 | def test_update_authorisation_token(self): |
114 | token_name_orig = 'Test_Token' |
115 | token_name_updated = 'Test_Token update' |
116 | diff --git a/src/maasserver/api/tests/test_machine.py b/src/maasserver/api/tests/test_machine.py |
117 | index 5b55eaf..e0b45ab 100644 |
118 | --- a/src/maasserver/api/tests/test_machine.py |
119 | +++ b/src/maasserver/api/tests/test_machine.py |
120 | @@ -716,10 +716,16 @@ class TestMachineAPI(APITestCase.ForUser): |
121 | } |
122 | self.client.post(self.get_machine_uri(machine), request) |
123 | self.assertThat( |
124 | - machine_method, MockCalledOnceWith( |
125 | - ANY, ANY, agent_name=ANY, |
126 | - bridge_all=False, bridge_fd=False, |
127 | - bridge_stp=False, comment=request['comment'])) |
128 | + machine_method, |
129 | + MockCalledOnceWith( |
130 | + ANY, |
131 | + agent_name=ANY, |
132 | + bridge_all=False, |
133 | + bridge_fd=0, |
134 | + bridge_stp=False, |
135 | + comment=request["comment"], |
136 | + ), |
137 | + ) |
138 | |
139 | def test_POST_deploy_passes_bridge_settings(self): |
140 | self.patch(node_module.Node, "_start") |
141 | @@ -740,10 +746,16 @@ class TestMachineAPI(APITestCase.ForUser): |
142 | } |
143 | self.client.post(self.get_machine_uri(machine), request) |
144 | self.assertThat( |
145 | - machine_method, MockCalledOnceWith( |
146 | - ANY, ANY, agent_name=ANY, |
147 | - bridge_all=True, bridge_fd=7, |
148 | - bridge_stp=True, comment=None)) |
149 | + machine_method, |
150 | + MockCalledOnceWith( |
151 | + ANY, |
152 | + agent_name=ANY, |
153 | + bridge_all=True, |
154 | + bridge_fd=7, |
155 | + bridge_stp=True, |
156 | + comment=None, |
157 | + ), |
158 | + ) |
159 | |
160 | def test_POST_release_releases_owned_machine(self): |
161 | self.patch(node_module.Machine, '_stop') |
162 | @@ -898,10 +910,16 @@ class TestMachineAPI(APITestCase.ForUser): |
163 | reverse('machines_handler'), |
164 | {'op': 'allocate', 'comment': comment}) |
165 | self.assertThat( |
166 | - machine_method, MockCalledOnceWith( |
167 | - ANY, ANY, agent_name=ANY, |
168 | - bridge_all=False, bridge_fd=False, |
169 | - bridge_stp=False, comment=comment)) |
170 | + machine_method, |
171 | + MockCalledOnceWith( |
172 | + ANY, |
173 | + agent_name=ANY, |
174 | + bridge_all=False, |
175 | + bridge_fd=False, |
176 | + bridge_stp=False, |
177 | + comment=comment, |
178 | + ), |
179 | + ) |
180 | |
181 | def test_POST_allocate_handles_missing_comment(self): |
182 | factory.make_Node( |
183 | @@ -911,10 +929,16 @@ class TestMachineAPI(APITestCase.ForUser): |
184 | self.client.post( |
185 | reverse('machines_handler'), {'op': 'allocate'}) |
186 | self.assertThat( |
187 | - machine_method, MockCalledOnceWith( |
188 | - ANY, ANY, agent_name=ANY, |
189 | - bridge_all=False, bridge_fd=False, |
190 | - bridge_stp=False, comment=None)) |
191 | + machine_method, |
192 | + MockCalledOnceWith( |
193 | + ANY, |
194 | + agent_name=ANY, |
195 | + bridge_all=False, |
196 | + bridge_fd=0, |
197 | + bridge_stp=False, |
198 | + comment=None, |
199 | + ), |
200 | + ) |
201 | |
202 | def test_POST_release_frees_hwe_kernel(self): |
203 | self.patch(node_module.Machine, '_stop') |
204 | diff --git a/src/maasserver/api/tests/test_machines.py b/src/maasserver/api/tests/test_machines.py |
205 | index b58f737..474ecaf 100644 |
206 | --- a/src/maasserver/api/tests/test_machines.py |
207 | +++ b/src/maasserver/api/tests/test_machines.py |
208 | @@ -1,4 +1,4 @@ |
209 | -# Copyright 2015-2016 Canonical Ltd. This software is licensed under the |
210 | +# Copyright 2015-2020 Canonical Ltd. This software is licensed under the |
211 | # GNU Affero General Public License version 3 (see the file LICENSE). |
212 | |
213 | """Tests for the machines API.""" |
214 | @@ -33,10 +33,6 @@ from maasserver.models import ( |
215 | node as node_module, |
216 | ) |
217 | from maasserver.models.node import RELEASABLE_STATUSES |
218 | -from maasserver.models.user import ( |
219 | - create_auth_token, |
220 | - get_auth_tokens, |
221 | -) |
222 | from maasserver.node_constraint_filter_forms import AcquireNodeForm |
223 | from maasserver.rpc.testing.fixtures import MockLiveRegionToClusterRPCFixture |
224 | from maasserver.testing.api import ( |
225 | @@ -546,29 +542,11 @@ class TestMachinesAPI(APITestCase.ForUser): |
226 | [machine.system_id for machine in machines], |
227 | extract_system_ids(parsed_result)) |
228 | |
229 | - def test_GET_list_allocated_returns_only_allocated_with_user_token(self): |
230 | - # If the user's allocated machines have different session tokens, |
231 | - # list_allocated should only return the machines that have the |
232 | - # current request's token on them. |
233 | - machine_1 = factory.make_Node( |
234 | - status=NODE_STATUS.ALLOCATED, owner=self.user, |
235 | - token=get_auth_tokens(self.user)[0]) |
236 | - second_token = create_auth_token(self.user) |
237 | - factory.make_Node( |
238 | - owner=self.user, status=NODE_STATUS.ALLOCATED, |
239 | - token=second_token) |
240 | - |
241 | - user_2 = factory.make_User() |
242 | - create_auth_token(user_2) |
243 | - factory.make_Node( |
244 | - owner=self.user, status=NODE_STATUS.ALLOCATED, |
245 | - token=second_token) |
246 | - |
247 | - # At this point we have two machines owned by the same user but |
248 | - # allocated with different tokens, and a third machine allocated to |
249 | - # someone else entirely. We expect list_allocated to |
250 | - # return the machine with the same token as the one used in |
251 | - # self.client, which is the one we set on machine_1 above. |
252 | + def test_GET_list_allocated_returns_only_allocated_with_user(self): |
253 | + machine = factory.make_Node( |
254 | + status=NODE_STATUS.ALLOCATED, owner=self.user |
255 | + ) |
256 | + factory.make_Node(status=NODE_STATUS.ALLOCATED) |
257 | |
258 | response = self.client.get(reverse('machines_handler'), { |
259 | 'op': 'list_allocated'}) |
260 | @@ -576,17 +554,17 @@ class TestMachinesAPI(APITestCase.ForUser): |
261 | parsed_result = json.loads( |
262 | response.content.decode(settings.DEFAULT_CHARSET)) |
263 | self.assertItemsEqual( |
264 | - [machine_1.system_id], extract_system_ids(parsed_result)) |
265 | + [machine.system_id], extract_system_ids(parsed_result) |
266 | + ) |
267 | |
268 | def test_GET_list_allocated_filters_by_id(self): |
269 | - # list_allocated takes an optional list of 'id' parameters to |
270 | - # filter returned results. |
271 | - current_token = get_auth_tokens(self.user)[0] |
272 | machines = [] |
273 | for _ in range(3): |
274 | - machines.append(factory.make_Node( |
275 | - status=NODE_STATUS.ALLOCATED, |
276 | - owner=self.user, token=current_token)) |
277 | + machines.append( |
278 | + factory.make_Node( |
279 | + status=NODE_STATUS.ALLOCATED, owner=self.user |
280 | + ) |
281 | + ) |
282 | |
283 | required_machine_ids = [machines[0].system_id, machines[1].system_id] |
284 | response = self.client.get(reverse('machines_handler'), { |
285 | @@ -1450,19 +1428,6 @@ class TestMachinesAPI(APITestCase.ForUser): |
286 | response.content.decode(settings.DEFAULT_CHARSET))['system_id'] |
287 | self.assertEqual(eligible_machine.system_id, system_id) |
288 | |
289 | - def test_POST_allocate_sets_a_token(self): |
290 | - # "acquire" should set the Token being used in the request on |
291 | - # the Machine that is allocated. |
292 | - available_status = NODE_STATUS.READY |
293 | - machine = factory.make_Node( |
294 | - status=available_status, owner=None, with_boot_disk=True) |
295 | - response = self.client.post( |
296 | - reverse('machines_handler'), {'op': 'allocate'}) |
297 | - self.assertThat(response, HasStatusCode(http.client.OK)) |
298 | - machine = Machine.objects.get(system_id=machine.system_id) |
299 | - oauth_key = self.client.token.key |
300 | - self.assertEqual(oauth_key, machine.token.key) |
301 | - |
302 | def test_POST_accept_gets_machine_out_of_declared_state(self): |
303 | # This will change when we add provisioning. Until then, |
304 | # acceptance gets a machine straight to Ready state. |
305 | diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py |
306 | index 1eb14d9..cb49543 100644 |
307 | --- a/src/maasserver/models/node.py |
308 | +++ b/src/maasserver/models/node.py |
309 | @@ -2551,11 +2551,16 @@ class Node(CleanSave, TimestampedModel): |
310 | "s" if len(missing_packages) > 1 else "")) |
311 | |
312 | def acquire( |
313 | - self, user, token=None, agent_name='', comment=None, |
314 | - bridge_all=False, bridge_stp=None, bridge_fd=None): |
315 | - """Mark commissioned node as acquired by the given user and token.""" |
316 | + self, |
317 | + user, |
318 | + agent_name="", |
319 | + comment=None, |
320 | + bridge_all=False, |
321 | + bridge_stp=None, |
322 | + bridge_fd=None, |
323 | + ): |
324 | + """Mark commissioned node as acquired by the given user.""" |
325 | assert self.owner is None or self.owner == user |
326 | - assert token is None or token.user == user |
327 | |
328 | self._create_acquired_filesystems() |
329 | self._register_request_event( |
330 | @@ -2564,7 +2569,6 @@ class Node(CleanSave, TimestampedModel): |
331 | self.status = NODE_STATUS.ALLOCATED |
332 | self.owner = user |
333 | self.agent_name = agent_name |
334 | - self.token = token |
335 | if bridge_all: |
336 | self._create_acquired_bridges( |
337 | bridge_stp=bridge_stp, bridge_fd=bridge_fd) |
338 | @@ -2823,8 +2827,7 @@ class Node(CleanSave, TimestampedModel): |
339 | finalize_release = True |
340 | |
341 | self.status = NODE_STATUS.RELEASING |
342 | - self.token = None |
343 | - self.agent_name = '' |
344 | + self.agent_name = "" |
345 | self.set_netboot() |
346 | self.osystem = '' |
347 | self.distro_series = '' |
348 | diff --git a/src/maasserver/models/tests/test_node.py b/src/maasserver/models/tests/test_node.py |
349 | index a47ee5d..193296e 100644 |
350 | --- a/src/maasserver/models/tests/test_node.py |
351 | +++ b/src/maasserver/models/tests/test_node.py |
352 | @@ -1,4 +1,4 @@ |
353 | -# Copyright 2012-2017 Canonical Ltd. This software is licensed under the |
354 | +# Copyright 2012-2020 Canonical Ltd. This software is licensed under the |
355 | # GNU Affero General Public License version 3 (see the file LICENSE). |
356 | |
357 | """Test maasserver models.""" |
358 | @@ -108,7 +108,6 @@ from maasserver.models.node import ( |
359 | ) |
360 | from maasserver.models.signals import power as node_query |
361 | from maasserver.models.timestampedmodel import now |
362 | -from maasserver.models.user import create_auth_token |
363 | from maasserver.node_status import ( |
364 | COMMISSIONING_LIKE_STATUSES, |
365 | NODE_FAILURE_MONITORED_STATUS_TRANSITIONS, |
366 | @@ -1113,12 +1112,6 @@ class TestNode(MAASServerTestCase): |
367 | node = factory.make_Node(bios_boot_method=factory.make_name("boot")) |
368 | self.assertEqual("pxe", node.get_bios_boot_method()) |
369 | |
370 | - def test_add_node_with_token(self): |
371 | - user = factory.make_User() |
372 | - token = create_auth_token(user) |
373 | - node = factory.make_Node(token=token) |
374 | - self.assertEqual(token, node.token) |
375 | - |
376 | def test_add_physical_interface(self): |
377 | mac = factory.make_mac_address() |
378 | node = factory.make_Node() |
379 | @@ -1560,9 +1553,8 @@ class TestNode(MAASServerTestCase): |
380 | def test_acquire(self): |
381 | node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True) |
382 | user = factory.make_User() |
383 | - token = create_auth_token(user) |
384 | - agent_name = factory.make_name('agent-name') |
385 | - node.acquire(user, token, agent_name) |
386 | + agent_name = factory.make_name("agent-name") |
387 | + node.acquire(user, agent_name) |
388 | self.assertEqual( |
389 | (user, NODE_STATUS.ALLOCATED, agent_name), |
390 | (node.owner, node.status, node.agent_name)) |
391 | @@ -1570,36 +1562,44 @@ class TestNode(MAASServerTestCase): |
392 | def test_acquire_calls__create_acquired_filesystems(self): |
393 | node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True) |
394 | user = factory.make_User() |
395 | - token = create_auth_token(user) |
396 | - agent_name = factory.make_name('agent-name') |
397 | + agent_name = factory.make_name("agent-name") |
398 | mock_create_acquired_filesystems = self.patch_autospec( |
399 | - node, "_create_acquired_filesystems") |
400 | - node.acquire(user, token, agent_name) |
401 | + node, "_create_acquired_filesystems" |
402 | + ) |
403 | + node.acquire(user, agent_name) |
404 | self.assertThat(mock_create_acquired_filesystems, MockCalledOnceWith()) |
405 | |
406 | def test_acquire_logs_user_request(self): |
407 | node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True) |
408 | user = factory.make_User() |
409 | - token = create_auth_token(user) |
410 | - agent_name = factory.make_name('agent-name') |
411 | - register_event = self.patch(node, '_register_request_event') |
412 | - node.acquire(user, token, agent_name) |
413 | - self.assertThat(register_event, MockCalledOnceWith( |
414 | - user, EVENT_TYPES.REQUEST_NODE_ACQUIRE, action='acquire', |
415 | - comment=None)) |
416 | + agent_name = factory.make_name("agent-name") |
417 | + register_event = self.patch(node, "_register_request_event") |
418 | + node.acquire(user, agent_name) |
419 | + self.assertThat( |
420 | + register_event, |
421 | + MockCalledOnceWith( |
422 | + user, |
423 | + EVENT_TYPES.REQUEST_NODE_ACQUIRE, |
424 | + action="acquire", |
425 | + comment=None, |
426 | + ), |
427 | + ) |
428 | |
429 | def test_acquire_calls__create_acquired_bridges(self): |
430 | node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True) |
431 | user = factory.make_User() |
432 | - token = create_auth_token(user) |
433 | - agent_name = factory.make_name('agent-name') |
434 | + agent_name = factory.make_name("agent-name") |
435 | mock_create_acquired_bridges = self.patch_autospec( |
436 | node, "_create_acquired_bridges") |
437 | bridge_stp = factory.pick_bool() |
438 | bridge_fd = random.randint(0, 500) |
439 | node.acquire( |
440 | - user, token, agent_name, |
441 | - bridge_all=True, bridge_stp=bridge_stp, bridge_fd=bridge_fd) |
442 | + user, |
443 | + agent_name, |
444 | + bridge_all=True, |
445 | + bridge_stp=bridge_stp, |
446 | + bridge_fd=bridge_fd, |
447 | + ) |
448 | self.assertThat( |
449 | mock_create_acquired_bridges, |
450 | MockCalledOnceWith(bridge_stp=bridge_stp, bridge_fd=bridge_fd)) |
451 | @@ -2102,8 +2102,7 @@ class TestNode(MAASServerTestCase): |
452 | MockCalledOnceWith(node.system_id, node.get_releasing_time())) |
453 | self.expectThat(node.status, Equals(NODE_STATUS.RELEASING)) |
454 | self.expectThat(node.owner, Equals(owner)) |
455 | - self.expectThat(node.agent_name, Equals('')) |
456 | - self.expectThat(node.token, Is(None)) |
457 | + self.expectThat(node.agent_name, Equals("")) |
458 | self.expectThat(node.netboot, Is(True)) |
459 | self.expectThat(node.osystem, Equals('')) |
460 | self.expectThat(node.distro_series, Equals('')) |
461 | @@ -2141,8 +2140,7 @@ class TestNode(MAASServerTestCase): |
462 | self.expectThat(Node._set_status_expires, MockNotCalled()) |
463 | self.expectThat(node.status, Equals(NODE_STATUS.RELEASING)) |
464 | self.expectThat(node.owner, Equals(owner)) |
465 | - self.expectThat(node.agent_name, Equals('')) |
466 | - self.expectThat(node.token, Is(None)) |
467 | + self.expectThat(node.agent_name, Equals("")) |
468 | self.expectThat(node.netboot, Is(True)) |
469 | self.expectThat(node.osystem, Equals('')) |
470 | self.expectThat(node.distro_series, Equals('')) |
471 | @@ -2169,8 +2167,7 @@ class TestNode(MAASServerTestCase): |
472 | self.expectThat(Node._set_status_expires, MockNotCalled()) |
473 | self.expectThat(node.status, Equals(NODE_STATUS.READY)) |
474 | self.expectThat(node.owner, Equals(None)) |
475 | - self.expectThat(node.agent_name, Equals('')) |
476 | - self.expectThat(node.token, Is(None)) |
477 | + self.expectThat(node.agent_name, Equals("")) |
478 | self.expectThat(node.netboot, Is(True)) |
479 | self.expectThat(node.osystem, Equals('')) |
480 | self.expectThat(node.distro_series, Equals('')) |
481 | diff --git a/src/maasserver/models/tests/test_userprofile.py b/src/maasserver/models/tests/test_userprofile.py |
482 | index 9fa2ba1..1a9bdc3 100644 |
483 | --- a/src/maasserver/models/tests/test_userprofile.py |
484 | +++ b/src/maasserver/models/tests/test_userprofile.py |
485 | @@ -1,4 +1,4 @@ |
486 | -# Copyright 2012-2016 Canonical Ltd. This software is licensed under the |
487 | +# Copyright 2012-2020 Canonical Ltd. This software is licensed under the |
488 | # GNU Affero General Public License version 3 (see the file LICENSE). |
489 | |
490 | """Tests for the UserProfile model.""" |
491 | @@ -85,6 +85,14 @@ class UserProfileTest(MAASServerTestCase): |
492 | self.assertFalse(Consumer.objects.filter(id__in=consumer_ids).exists()) |
493 | self.assertFalse(Token.objects.filter(id__in=token_ids).exists()) |
494 | |
495 | + def test_delete_doesnt_delete_node(self): |
496 | + user = factory.make_User() |
497 | + profile = user.userprofile |
498 | + _, token = profile.create_authorisation_token() |
499 | + node = factory.make_Node(owner=user, token=token) |
500 | + profile.delete_authorisation_token(token.key) |
501 | + self.assertIsNotNone(reload_object(node)) |
502 | + |
503 | def test_delete_deletes_related_filestorage_objects(self): |
504 | # Deleting a profile deletes the related filestorage objects. |
505 | profile = factory.make_User().userprofile |
506 | diff --git a/src/maasserver/models/userprofile.py b/src/maasserver/models/userprofile.py |
507 | index fcffb8d..aaa302f 100644 |
508 | --- a/src/maasserver/models/userprofile.py |
509 | +++ b/src/maasserver/models/userprofile.py |
510 | @@ -1,4 +1,4 @@ |
511 | -# Copyright 2012-2016 Canonical Ltd. This software is licensed under the |
512 | +# Copyright 2012-2020 Canonical Ltd. This software is licensed under the |
513 | # GNU Affero General Public License version 3 (see the file LICENSE). |
514 | |
515 | """UserProfile model.""" |
516 | @@ -19,6 +19,7 @@ from django.db.models import ( |
517 | from django.shortcuts import get_object_or_404 |
518 | from maasserver import DefaultMeta |
519 | from maasserver.exceptions import CannotDeleteUserException |
520 | +from maasserver.models import Node |
521 | from maasserver.models.cleansave import CleanSave |
522 | from piston3.models import Token |
523 | |
524 | @@ -139,7 +140,11 @@ class UserProfile(CleanSave, Model): |
525 | |
526 | """ |
527 | token = get_object_or_404( |
528 | - Token, user=self.user, token_type=Token.ACCESS, key=token_key) |
529 | + Token, user=self.user, token_type=Token.ACCESS, key=token_key, |
530 | + ) |
531 | + # LP:1870171 - Make sure the key being deleted isn't assoicated with |
532 | + # any node. In MAAS 2.8+ Node.token has been removed. |
533 | + Node.objects.filter(token=token).update(token=None) |
534 | token.consumer.delete() |
535 | token.delete() |
536 | |
537 | diff --git a/src/maasserver/node_action.py b/src/maasserver/node_action.py |
538 | index 74c8df7..f034453 100644 |
539 | --- a/src/maasserver/node_action.py |
540 | +++ b/src/maasserver/node_action.py |
541 | @@ -311,7 +311,10 @@ class Acquire(NodeAction): |
542 | def execute(self): |
543 | """See `NodeAction.execute`.""" |
544 | with locks.node_acquire: |
545 | - self.node.acquire(self.user, token=None) |
546 | + try: |
547 | + self.node.acquire(self.user) |
548 | + except ValidationError as e: |
549 | + raise NodeActionError(e) |
550 | |
551 | |
552 | class Deploy(NodeAction): |
553 | @@ -327,7 +330,10 @@ class Deploy(NodeAction): |
554 | """See `NodeAction.execute`.""" |
555 | if self.node.owner is None: |
556 | with locks.node_acquire: |
557 | - self.node.acquire(self.user, token=None) |
558 | + try: |
559 | + self.node.acquire(self.user) |
560 | + except ValidationError as e: |
561 | + raise NodeActionError(e) |
562 | |
563 | if osystem and distro_series: |
564 | try: |
Approved in https:/ /code.launchpad .net/~ltrager/ maas/+git/ maas/+merge/ 381876