Merge ~ltrager/maas:lp1870171 into maas:master

Proposed by Lee Trager
Status: Merged
Approved by: Lee Trager
Approved revision: 432c4c8f40c7b559c538fdb6432352f63dd7b07d
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ltrager/maas:lp1870171
Merge into: maas:master
Diff against target: 435 lines (+33/-101)
8 files modified
src/maasserver/api/machines.py (+5/-6)
src/maasserver/api/tests/test_machine.py (+0/-4)
src/maasserver/api/tests/test_machines.py (+8/-52)
src/maasserver/migrations/maasserver/0206_remove_node_token.py (+12/-0)
src/maasserver/models/node.py (+1/-15)
src/maasserver/models/tests/test_node.py (+4/-19)
src/maasserver/node_action.py (+2/-2)
src/maasserver/tests/test_node_action.py (+1/-3)
Reviewer Review Type Date Requested Status
Alberto Donato (community) Approve
MAAS Lander Approve
Review via email: mp+381876@code.launchpad.net

Commit message

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 assoication
causes the Node to be deleted as well. Allocated machines are already
filtered by owner and status so the token column isn't needed.

To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lp1870171 lp:~ltrager/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 432c4c8f40c7b559c538fdb6432352f63dd7b07d

review: Approve
Revision history for this message
Alberto Donato (ack) wrote :

Have we confirmed this doesn't break juju?

This is a change in behavior of the list-allocated call since it now returns all allocated machines for the user, where before it only returned the ones allocated with the specific key.

I think that's fine (and in fact saner) for users, but I wonder if juju relies on only seeing machines for the specific model (the UI used to mention that you need to create an API key for each juju environment/model).

review: Needs Information
Revision history for this message
Lee Trager (ltrager) wrote :

Spoke with Rick on IRC and it seems like this should be okay.

<ltrager> when Juju lists allocated machines in MAAS does it expect that list to contain all the allocated assoicated with that user's OAUTH token or just that user?
<rick_h> ltrager: you mean when you juju status?
<rick_h> ltrager: and since the oauth token is per user I'm not sure the difference?
<ltrager> rick_h: A user can have multiple OAUTH keys in MAAS. On the API only, MAAS assoicated an allocated machine with the OAUTH token being used. When listing allocated machines the API only lists machines allocated with that key.
<rick_h> ltrager: right, so that oauth token is how Juju communicates with MAAS and so won't see anything that the API key can't see
<ltrager> rick_h: this is the only place in MAAS that associates the API OAUTH key with a Node and we'd like to remove that association as its causing a bug
<ltrager> rick_h: right now if you have two OAUTH keys for a user and OAUTH1 allocates a node OAUTH2 won't see it even if they are the same user
<ltrager> rick_h: that seems like a bug which I'm trying to fix but it was brought up Juju might be expecting that behavior
<rick_h> ltrager: hmmm, so in juju land if you wanted to update tokens I'd expect you to be able to juju upgrade-credential on the contrller, supply the new token, and as long as the new token has access to the same hardware it'd be ok?
<ltrager> rick_h: currently that isn't the case
<rick_h> ltrager: on which end? MAAS or Juju?
<ltrager> rick_h: the bug I'm trying to fix is that if you allocate a node with an OAUTH key and delete it it also deletes any node assoicated with it
<ltrager> rick_h: MAAS
<rick_h> ltrager: ok, I don't believe we're relying on that in any way as we don't manage the oauth keys in Juju. We just use them to make calls to MAAS.
<ltrager> rick_h: ok thanks!

Revision history for this message
Alberto Donato (ack) wrote :

LGTM, +1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/api/machines.py b/src/maasserver/api/machines.py
2index 7a983c2..72281b4 100644
3--- a/src/maasserver/api/machines.py
4+++ b/src/maasserver/api/machines.py
5@@ -1,4 +1,4 @@
6-# Copyright 2015-2019 Canonical Ltd. This software is licensed under the
7+# Copyright 2015-2020 Canonical Ltd. This software is licensed under the
8 # GNU Affero General Public License version 3 (see the file LICENSE).
9
10 __all__ = [
11@@ -40,7 +40,6 @@ from maasserver.api.nodes import (
12 from maasserver.api.support import admin_method, operation
13 from maasserver.api.utils import (
14 get_mandatory_param,
15- get_oauth_token,
16 get_optional_list,
17 get_optional_param,
18 )
19@@ -746,7 +745,6 @@ class MachineHandler(NodeHandler, OwnerDataMixin, PowerMixin):
20 )
21 machine.acquire(
22 request.user,
23- get_oauth_token(request),
24 agent_name=options.agent_name,
25 comment=options.comment,
26 bridge_all=options.bridge_all,
27@@ -2139,7 +2137,7 @@ class MachinesHandler(NodesHandler, PowersMixin):
28 @operation(idempotent=True)
29 def list_allocated(self, request):
30 """@description-title List allocated
31- @description List machines that were allocated to the User/oauth token.
32+ @description List machines that were allocated to the User.
33
34 @success (http-status-code) "200" 200
35 @success (json) "success-json" A JSON object containing a list of
36@@ -2149,7 +2147,9 @@ class MachinesHandler(NodesHandler, PowersMixin):
37 """
38 # limit to machines that the user can view
39 machines = Machine.objects.get_nodes(request.user, NodePermission.view)
40- machines = machines.filter(token=get_oauth_token(request))
41+ machines = machines.filter(
42+ owner=request.user, status=NODE_STATUS.ALLOCATED
43+ )
44 system_ids = get_optional_list(request.GET, "id")
45 if system_ids:
46 machines = machines.filter(system_id__in=system_ids)
47@@ -2472,7 +2472,6 @@ class MachinesHandler(NodesHandler, PowersMixin):
48 if not dry_run:
49 machine.acquire(
50 request.user,
51- get_oauth_token(request),
52 agent_name=options.agent_name,
53 comment=options.comment,
54 bridge_all=options.bridge_all,
55diff --git a/src/maasserver/api/tests/test_machine.py b/src/maasserver/api/tests/test_machine.py
56index 8b62ea3..d4e4370 100644
57--- a/src/maasserver/api/tests/test_machine.py
58+++ b/src/maasserver/api/tests/test_machine.py
59@@ -911,7 +911,6 @@ class TestMachineAPI(APITestCase.ForUser):
60 machine_method,
61 MockCalledOnceWith(
62 ANY,
63- ANY,
64 agent_name=ANY,
65 bridge_all=False,
66 bridge_type=BRIDGE_TYPE.STANDARD,
67@@ -947,7 +946,6 @@ class TestMachineAPI(APITestCase.ForUser):
68 machine_method,
69 MockCalledOnceWith(
70 ANY,
71- ANY,
72 agent_name=ANY,
73 bridge_all=True,
74 bridge_type=BRIDGE_TYPE.OVS,
75@@ -1386,7 +1384,6 @@ class TestMachineAPI(APITestCase.ForUser):
76 machine_method,
77 MockCalledOnceWith(
78 ANY,
79- ANY,
80 agent_name=ANY,
81 bridge_all=False,
82 bridge_type=BRIDGE_TYPE.STANDARD,
83@@ -1409,7 +1406,6 @@ class TestMachineAPI(APITestCase.ForUser):
84 machine_method,
85 MockCalledOnceWith(
86 ANY,
87- ANY,
88 agent_name=ANY,
89 bridge_all=False,
90 bridge_type=BRIDGE_TYPE.STANDARD,
91diff --git a/src/maasserver/api/tests/test_machines.py b/src/maasserver/api/tests/test_machines.py
92index b6a417c..d293824 100644
93--- a/src/maasserver/api/tests/test_machines.py
94+++ b/src/maasserver/api/tests/test_machines.py
95@@ -1,4 +1,4 @@
96-# Copyright 2015-2019 Canonical Ltd. This software is licensed under the
97+# Copyright 2015-2020 Canonical Ltd. This software is licensed under the
98 # GNU Affero General Public License version 3 (see the file LICENSE).
99
100 """Tests for the machines API."""
101@@ -30,7 +30,6 @@ from maasserver.forms.pods import ComposeMachineForm, ComposeMachineForPodsForm
102 from maasserver.models import Config, Domain, Machine, Node
103 from maasserver.models import node as node_module
104 from maasserver.models.node import RELEASABLE_STATUSES
105-from maasserver.models.user import create_auth_token, get_auth_tokens
106 from maasserver.node_constraint_filter_forms import AcquireNodeForm
107 from maasserver.rpc.testing.fixtures import MockLiveRegionToClusterRPCFixture
108 from maasserver.testing.api import APITestCase, APITransactionTestCase
109@@ -694,31 +693,11 @@ class TestMachinesAPI(APITestCase.ForUser):
110 extract_system_ids(parsed_result),
111 )
112
113- def test_GET_list_allocated_returns_only_allocated_with_user_token(self):
114- # If the user's allocated machines have different session tokens,
115- # list_allocated should only return the machines that have the
116- # current request's token on them.
117- machine_1 = factory.make_Node(
118- status=NODE_STATUS.ALLOCATED,
119- owner=self.user,
120- token=get_auth_tokens(self.user)[0],
121- )
122- second_token = create_auth_token(self.user)
123- factory.make_Node(
124- owner=self.user, status=NODE_STATUS.ALLOCATED, token=second_token
125- )
126-
127- user_2 = factory.make_User()
128- create_auth_token(user_2)
129- factory.make_Node(
130- owner=self.user, status=NODE_STATUS.ALLOCATED, token=second_token
131+ def test_GET_list_allocated_returns_only_allocated_with_user(self):
132+ machine = factory.make_Node(
133+ status=NODE_STATUS.ALLOCATED, owner=self.user
134 )
135-
136- # At this point we have two machines owned by the same user but
137- # allocated with different tokens, and a third machine allocated to
138- # someone else entirely. We expect list_allocated to
139- # return the machine with the same token as the one used in
140- # self.client, which is the one we set on machine_1 above.
141+ factory.make_Node(status=NODE_STATUS.ALLOCATED)
142
143 response = self.client.get(
144 reverse("machines_handler"), {"op": "list_allocated"}
145@@ -728,20 +707,15 @@ class TestMachinesAPI(APITestCase.ForUser):
146 response.content.decode(settings.DEFAULT_CHARSET)
147 )
148 self.assertItemsEqual(
149- [machine_1.system_id], extract_system_ids(parsed_result)
150+ [machine.system_id], extract_system_ids(parsed_result)
151 )
152
153 def test_GET_list_allocated_filters_by_id(self):
154- # list_allocated takes an optional list of 'id' parameters to
155- # filter returned results.
156- current_token = get_auth_tokens(self.user)[0]
157 machines = []
158 for _ in range(3):
159 machines.append(
160 factory.make_Node(
161- status=NODE_STATUS.ALLOCATED,
162- owner=self.user,
163- token=current_token,
164+ status=NODE_STATUS.ALLOCATED, owner=self.user
165 )
166 )
167
168@@ -771,20 +745,17 @@ class TestMachinesAPI(APITestCase.ForUser):
169 rbac.store.add_pool(pool)
170 rbac.store.allow(self.user.username, pool, "view")
171
172- token = get_auth_tokens(self.user)[0]
173 factory.make_Node(
174 hostname="viewable",
175 owner=self.user,
176- token=token,
177 pool=pool,
178 status=NODE_STATUS.ALLOCATED,
179 )
180- # a machine with the same token but not accesssible to the user (not in
181+ # a machine with the same user but not accesssible to the user (not in
182 # the allowed pool)
183 factory.make_Node(
184 hostname="not-accessible",
185 owner=self.user,
186- token=token,
187 status=NODE_STATUS.ALLOCATED,
188 )
189 # a machine owned by another user in the accessible pool
190@@ -2260,21 +2231,6 @@ class TestMachinesAPI(APITestCase.ForUser):
191 )["system_id"]
192 self.assertEqual(node2.system_id, system_id)
193
194- def test_POST_allocate_sets_a_token(self):
195- # "acquire" should set the Token being used in the request on
196- # the Machine that is allocated.
197- available_status = NODE_STATUS.READY
198- machine = factory.make_Node(
199- status=available_status, owner=None, with_boot_disk=True
200- )
201- response = self.client.post(
202- reverse("machines_handler"), {"op": "allocate"}
203- )
204- self.assertThat(response, HasStatusCode(http.client.OK))
205- machine = Machine.objects.get(system_id=machine.system_id)
206- oauth_key = self.client.token.key
207- self.assertEqual(oauth_key, machine.token.key)
208-
209 def test_POST_accept_gets_machine_out_of_declared_state(self):
210 # This will change when we add provisioning. Until then,
211 # acceptance gets a machine straight to Ready state.
212diff --git a/src/maasserver/migrations/maasserver/0206_remove_node_token.py b/src/maasserver/migrations/maasserver/0206_remove_node_token.py
213new file mode 100644
214index 0000000..3a09b11
215--- /dev/null
216+++ b/src/maasserver/migrations/maasserver/0206_remove_node_token.py
217@@ -0,0 +1,12 @@
218+# -*- coding: utf-8 -*-
219+# Generated by Django 1.11.11 on 2020-04-08 03:28
220+from __future__ import unicode_literals
221+
222+from django.db import migrations
223+
224+
225+class Migration(migrations.Migration):
226+
227+ dependencies = [("maasserver", "0205_pod_nodes")]
228+
229+ operations = [migrations.RemoveField(model_name="node", name="token")]
230diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py
231index 3f0b1bb..ef8085c 100644
232--- a/src/maasserver/models/node.py
233+++ b/src/maasserver/models/node.py
234@@ -61,7 +61,6 @@ from django.db.models.query import QuerySet
235 from django.shortcuts import get_object_or_404
236 from netaddr import IPAddress, IPNetwork
237 import petname
238-from piston3.models import Token
239 from twisted.internet import reactor
240 from twisted.internet.defer import (
241 Deferred,
242@@ -1092,15 +1091,6 @@ class Node(CleanSave, TimestampedModel):
243 null=True, blank=False, default=None, editable=False
244 )
245
246- token = ForeignKey(
247- Token,
248- db_index=True,
249- null=True,
250- editable=False,
251- unique=False,
252- on_delete=CASCADE,
253- )
254-
255 error = CharField(max_length=255, blank=True, default="")
256
257 netboot = BooleanField(default=True)
258@@ -3203,7 +3193,6 @@ class Node(CleanSave, TimestampedModel):
259 def acquire(
260 self,
261 user,
262- token=None,
263 agent_name="",
264 comment=None,
265 bridge_all=False,
266@@ -3211,9 +3200,8 @@ class Node(CleanSave, TimestampedModel):
267 bridge_stp=None,
268 bridge_fd=None,
269 ):
270- """Mark commissioned node as acquired by the given user and token."""
271+ """Mark commissioned node as acquired by the given user."""
272 assert self.owner is None or self.owner == user
273- assert token is None or token.user == user
274
275 self._create_acquired_filesystems()
276 self._register_request_event(
277@@ -3225,7 +3213,6 @@ class Node(CleanSave, TimestampedModel):
278 self.status = NODE_STATUS.ALLOCATED
279 self.owner = user
280 self.agent_name = agent_name
281- self.token = token
282 if bridge_all:
283 self._create_acquired_bridges(
284 bridge_type=bridge_type,
285@@ -3559,7 +3546,6 @@ class Node(CleanSave, TimestampedModel):
286 finalize_release = True
287
288 self.status = NODE_STATUS.RELEASING
289- self.token = None
290 self.agent_name = ""
291 self.set_netboot()
292 self.set_ephemeral_deploy()
293diff --git a/src/maasserver/models/tests/test_node.py b/src/maasserver/models/tests/test_node.py
294index 26ccf9e..bf7cb93 100644
295--- a/src/maasserver/models/tests/test_node.py
296+++ b/src/maasserver/models/tests/test_node.py
297@@ -1,4 +1,4 @@
298-# Copyright 2012-2019 Canonical Ltd. This software is licensed under the
299+# Copyright 2012-2020 Canonical Ltd. This software is licensed under the
300 # GNU Affero General Public License version 3 (see the file LICENSE).
301
302 """Test maasserver models."""
303@@ -125,7 +125,6 @@ from maasserver.models.partitiontable import PARTITION_TABLE_EXTRA_SPACE
304 from maasserver.models.resourcepool import ResourcePool
305 from maasserver.models.signals import power as node_query
306 from maasserver.models.timestampedmodel import now
307-from maasserver.models.user import create_auth_token
308 from maasserver.node_status import (
309 COMMISSIONING_LIKE_STATUSES,
310 get_node_timeout,
311@@ -1358,12 +1357,6 @@ class TestNode(MAASServerTestCase):
312 node = factory.make_Node(bios_boot_method=factory.make_name("boot"))
313 self.assertEqual("pxe", node.get_bios_boot_method())
314
315- def test_add_node_with_token(self):
316- user = factory.make_User()
317- token = create_auth_token(user)
318- node = factory.make_Node(token=token)
319- self.assertEqual(token, node.token)
320-
321 def test_add_physical_interface(self):
322 mac = factory.make_mac_address()
323 node = factory.make_Node()
324@@ -1885,9 +1878,8 @@ class TestNode(MAASServerTestCase):
325 def test_acquire(self):
326 node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True)
327 user = factory.make_User()
328- token = create_auth_token(user)
329 agent_name = factory.make_name("agent-name")
330- node.acquire(user, token, agent_name)
331+ node.acquire(user, agent_name)
332 self.assertEqual(
333 (user, NODE_STATUS.ALLOCATED, agent_name),
334 (node.owner, node.status, node.agent_name),
335@@ -1896,21 +1888,19 @@ class TestNode(MAASServerTestCase):
336 def test_acquire_calls__create_acquired_filesystems(self):
337 node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True)
338 user = factory.make_User()
339- token = create_auth_token(user)
340 agent_name = factory.make_name("agent-name")
341 mock_create_acquired_filesystems = self.patch_autospec(
342 node, "_create_acquired_filesystems"
343 )
344- node.acquire(user, token, agent_name)
345+ node.acquire(user, agent_name)
346 self.assertThat(mock_create_acquired_filesystems, MockCalledOnceWith())
347
348 def test_acquire_logs_user_request(self):
349 node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True)
350 user = factory.make_User()
351- token = create_auth_token(user)
352 agent_name = factory.make_name("agent-name")
353 register_event = self.patch(node, "_register_request_event")
354- node.acquire(user, token, agent_name)
355+ node.acquire(user, agent_name)
356 self.assertThat(
357 register_event,
358 MockCalledOnceWith(
359@@ -1924,7 +1914,6 @@ class TestNode(MAASServerTestCase):
360 def test_acquire_calls__create_acquired_bridges(self):
361 node = factory.make_Node(status=NODE_STATUS.READY, with_boot_disk=True)
362 user = factory.make_User()
363- token = create_auth_token(user)
364 agent_name = factory.make_name("agent-name")
365 mock_create_acquired_bridges = self.patch_autospec(
366 node, "_create_acquired_bridges"
367@@ -1934,7 +1923,6 @@ class TestNode(MAASServerTestCase):
368 bridge_fd = random.randint(0, 500)
369 node.acquire(
370 user,
371- token,
372 agent_name,
373 bridge_all=True,
374 bridge_type=bridge_type,
375@@ -2546,7 +2534,6 @@ class TestNode(MAASServerTestCase):
376 self.expectThat(node.status, Equals(NODE_STATUS.RELEASING))
377 self.expectThat(node.owner, Equals(owner))
378 self.expectThat(node.agent_name, Equals(""))
379- self.expectThat(node.token, Is(None))
380 self.expectThat(node.netboot, Is(True))
381 self.expectThat(node.ephemeral_deploy, Is(False))
382 self.expectThat(node.osystem, Equals(""))
383@@ -2592,7 +2579,6 @@ class TestNode(MAASServerTestCase):
384 self.expectThat(node.status, Equals(NODE_STATUS.RELEASING))
385 self.expectThat(node.owner, Equals(owner))
386 self.expectThat(node.agent_name, Equals(""))
387- self.expectThat(node.token, Is(None))
388 self.expectThat(node.netboot, Is(True))
389 self.expectThat(node.ephemeral_deploy, Is(False))
390 self.expectThat(node.osystem, Equals(""))
391@@ -2625,7 +2611,6 @@ class TestNode(MAASServerTestCase):
392 self.expectThat(node.status, Equals(NODE_STATUS.READY))
393 self.expectThat(node.owner, Equals(None))
394 self.expectThat(node.agent_name, Equals(""))
395- self.expectThat(node.token, Is(None))
396 self.expectThat(node.netboot, Is(True))
397 self.expectThat(node.ephemeral_deploy, Is(False))
398 self.expectThat(node.osystem, Equals(""))
399diff --git a/src/maasserver/node_action.py b/src/maasserver/node_action.py
400index 831585c..0e5af52 100644
401--- a/src/maasserver/node_action.py
402+++ b/src/maasserver/node_action.py
403@@ -448,7 +448,7 @@ class Acquire(NodeAction):
404 """See `NodeAction.execute`."""
405 with locks.node_acquire:
406 try:
407- self.node.acquire(self.user, token=None)
408+ self.node.acquire(self.user)
409 except ValidationError as e:
410 raise NodeActionError(e)
411
412@@ -486,7 +486,7 @@ class Deploy(NodeAction):
413 if self.node.owner is None:
414 with locks.node_acquire:
415 try:
416- self.node.acquire(self.user, token=None)
417+ self.node.acquire(self.user)
418 except ValidationError as e:
419 raise NodeActionError(e)
420 if install_kvm:
421diff --git a/src/maasserver/tests/test_node_action.py b/src/maasserver/tests/test_node_action.py
422index b7278b7..6d438f0 100644
423--- a/src/maasserver/tests/test_node_action.py
424+++ b/src/maasserver/tests/test_node_action.py
425@@ -792,9 +792,7 @@ class TestDeployAction(MAASServerTestCase):
426 Deploy(node, user, request).execute(**extra)
427 self.expectThat(mock_get_curtin_config, MockCalledOnceWith(ANY, node))
428 # Make sure we set bridge_all when we acquire the Node.
429- self.expectThat(
430- mock_node_acquire, MockCalledOnceWith(user, token=None)
431- )
432+ self.expectThat(mock_node_acquire, MockCalledOnceWith(user))
433 self.expectThat(mock_node_start, MockCalledOnceWith(user))
434 # Make sure ubuntu/bionic is selected if KVM pod deployment has been
435 # selected.

Subscribers

People subscribed via source and target branches