Merge lp:~allenap/maas/check-for-rpc-connection-before-registering into lp:~maas-committers/maas/trunk

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 3168
Proposed branch: lp:~allenap/maas/check-for-rpc-connection-before-registering
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 136 lines (+53/-3)
4 files modified
src/maasserver/api/node_groups.py (+13/-1)
src/maasserver/api/tests/test_register.py (+21/-1)
src/provisioningserver/start_cluster_controller.py (+9/-1)
src/provisioningserver/tests/test_start_cluster_controller.py (+10/-0)
To merge this branch: bzr merge lp:~allenap/maas/check-for-rpc-connection-before-registering
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve
Review via email: mp+236803@code.launchpad.net

Commit message

Check for an RPC connection before registering a new cluster.

To post a comment you must log in.
Revision history for this message
Raphaël Badin (rvb) wrote :

Looks good, just one remark about the choice of httplib.GATEWAY_TIMEOUT.

review: Approve
Revision history for this message
Gavin Panella (allenap) wrote :

Thanks!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/api/node_groups.py'
--- src/maasserver/api/node_groups.py 2014-09-24 14:20:51 +0000
+++ src/maasserver/api/node_groups.py 2014-10-02 01:12:08 +0000
@@ -55,11 +55,13 @@
55from maasserver.models.node import Node55from maasserver.models.node import Node
56from maasserver.models.nodegroup import NodeGroup56from maasserver.models.nodegroup import NodeGroup
57from maasserver.models.nodeprobeddetails import get_probed_details57from maasserver.models.nodeprobeddetails import get_probed_details
58from maasserver.rpc import getClientFor
58from maasserver.utils import (59from maasserver.utils import (
59 build_absolute_uri,60 build_absolute_uri,
60 get_local_cluster_UUID,61 get_local_cluster_UUID,
61 )62 )
62from maasserver.utils.orm import get_one63from maasserver.utils.orm import get_one
64from provisioningserver.rpc.exceptions import NoConnectionsAvailable
6365
6466
65DISPLAYED_NODEGROUP_FIELDS = ('uuid', 'status', 'name', 'cluster_name')67DISPLAYED_NODEGROUP_FIELDS = ('uuid', 'status', 'name', 'cluster_name')
@@ -180,7 +182,17 @@
180 # send it from now on.182 # send it from now on.
181 update_nodegroup_maas_url(nodegroup, request)183 update_nodegroup_maas_url(nodegroup, request)
182 else:184 else:
183 nodegroup = register_nodegroup(request, uuid)185 # An RPC connection to the target cluster is required for
186 # registration to work, so we wait up to 10 seconds for a
187 # connection to be established.
188 try:
189 getClientFor(uuid, timeout=10)
190 except NoConnectionsAvailable:
191 return HttpResponse(
192 "Waiting for RPC connection.",
193 status=httplib.SERVICE_UNAVAILABLE)
194 else:
195 nodegroup = register_nodegroup(request, uuid)
184196
185 return compose_nodegroup_register_response(nodegroup, already_existed)197 return compose_nodegroup_register_response(nodegroup, already_existed)
186198
187199
=== modified file 'src/maasserver/api/tests/test_register.py'
--- src/maasserver/api/tests/test_register.py 2014-09-24 14:20:51 +0000
+++ src/maasserver/api/tests/test_register.py 2014-10-02 01:12:08 +0000
@@ -42,7 +42,11 @@
42 ANY,42 ANY,
43 Mock,43 Mock,
44 )44 )
45from testtools.matchers import MatchesStructure45from provisioningserver.rpc.exceptions import NoConnectionsAvailable
46from testtools.matchers import (
47 Equals,
48 MatchesStructure,
49 )
46from testtools.testcase import ExpectedException50from testtools.testcase import ExpectedException
4751
4852
@@ -210,6 +214,11 @@
210 This method can be called anonymously.214 This method can be called anonymously.
211 """215 """
212216
217 def setUp(self):
218 super(TestRegisterAPI, self).setUp()
219 self.getClientFor = self.patch_autospec(
220 nodegroups_module, "getClientFor")
221
213 def test_register_creates_nodegroup_and_interfaces(self):222 def test_register_creates_nodegroup_and_interfaces(self):
214 create_configured_master()223 create_configured_master()
215 name = factory.make_name('cluster')224 name = factory.make_name('cluster')
@@ -443,3 +452,14 @@
443 self.assertEqual([], update_maas_url.call_args_list)452 self.assertEqual([], update_maas_url.call_args_list)
444 # The master's maas_url field remains empty.453 # The master's maas_url field remains empty.
445 self.assertEqual("", master.maas_url)454 self.assertEqual("", master.maas_url)
455
456 def test_register_nodegroup_when_rpc_not_connected(self):
457 create_configured_master()
458 self.getClientFor.side_effect = NoConnectionsAvailable
459 response = self.client.post(
460 reverse('nodegroups_handler'),
461 {'op': 'register', 'uuid': factory.make_UUID()})
462 self.expectThat(
463 response.status_code, Equals(httplib.SERVICE_UNAVAILABLE))
464 self.expectThat(
465 response.content, Equals("Waiting for RPC connection."))
446466
=== modified file 'src/provisioningserver/start_cluster_controller.py'
--- src/provisioningserver/start_cluster_controller.py 2014-09-23 20:16:54 +0000
+++ src/provisioningserver/start_cluster_controller.py 2014-10-02 01:12:08 +0000
@@ -72,7 +72,12 @@
72 :raise ClusterControllerRejected: if this system has been rejected as a72 :raise ClusterControllerRejected: if this system has been rejected as a
73 cluster controller.73 cluster controller.
74 """74 """
75 known_responses = {httplib.OK, httplib.FORBIDDEN, httplib.ACCEPTED}75 known_responses = {
76 httplib.ACCEPTED,
77 httplib.FORBIDDEN,
78 httplib.OK,
79 httplib.SERVICE_UNAVAILABLE,
80 }
7681
77 interfaces = json.dumps(discover_networks())82 interfaces = json.dumps(discover_networks())
78 client = make_anonymous_api_client(server_url)83 client = make_anonymous_api_client(server_url)
@@ -100,6 +105,9 @@
100 elif status_code == httplib.ACCEPTED:105 elif status_code == httplib.ACCEPTED:
101 # Our application is still waiting for approval. Keep trying.106 # Our application is still waiting for approval. Keep trying.
102 return None107 return None
108 elif status_code == httplib.SERVICE_UNAVAILABLE:
109 # Our cluster has not yet established RPC connections.
110 return None
103 elif status_code == httplib.FORBIDDEN:111 elif status_code == httplib.FORBIDDEN:
104 # Our application has been rejected. Give up.112 # Our application has been rejected. Give up.
105 raise ClusterControllerRejected(113 raise ClusterControllerRejected(
106114
=== modified file 'src/provisioningserver/tests/test_start_cluster_controller.py'
--- src/provisioningserver/tests/test_start_cluster_controller.py 2014-09-23 20:21:50 +0000
+++ src/provisioningserver/tests/test_start_cluster_controller.py 2014-10-02 01:12:08 +0000
@@ -123,6 +123,10 @@
123 """Prepare to return "request pending" from API request."""123 """Prepare to return "request pending" from API request."""
124 self.prepare_response(httplib.ACCEPTED)124 self.prepare_response(httplib.ACCEPTED)
125125
126 def prepare_rpc_wait_response(self):
127 """Prepare to return "waiting for RPC" from API request."""
128 self.prepare_response(httplib.SERVICE_UNAVAILABLE)
129
126 def test_run_command(self):130 def test_run_command(self):
127 # We can't really run the script, but we can verify that (with131 # We can't really run the script, but we can verify that (with
128 # the right system functions patched out) we can run it132 # the right system functions patched out) we can run it
@@ -154,6 +158,12 @@
154 Sleeping,158 Sleeping,
155 start_cluster_controller.run, make_args())159 start_cluster_controller.run, make_args())
156160
161 def test_polls_on_rpc_wait(self):
162 self.prepare_rpc_wait_response()
163 self.assertRaises(
164 Sleeping,
165 start_cluster_controller.run, make_args())
166
157 def test_polls_on_unexpected_errors(self):167 def test_polls_on_unexpected_errors(self):
158 self.patch(MAASDispatcher, 'dispatch_query').side_effect = HTTPError(168 self.patch(MAASDispatcher, 'dispatch_query').side_effect = HTTPError(
159 make_url(), httplib.REQUEST_TIMEOUT, "Timeout.", '', BytesIO())169 make_url(), httplib.REQUEST_TIMEOUT, "Timeout.", '', BytesIO())