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
1=== modified file 'src/maasserver/api/node_groups.py'
2--- src/maasserver/api/node_groups.py 2014-09-24 14:20:51 +0000
3+++ src/maasserver/api/node_groups.py 2014-10-02 01:12:08 +0000
4@@ -55,11 +55,13 @@
5 from maasserver.models.node import Node
6 from maasserver.models.nodegroup import NodeGroup
7 from maasserver.models.nodeprobeddetails import get_probed_details
8+from maasserver.rpc import getClientFor
9 from maasserver.utils import (
10 build_absolute_uri,
11 get_local_cluster_UUID,
12 )
13 from maasserver.utils.orm import get_one
14+from provisioningserver.rpc.exceptions import NoConnectionsAvailable
15
16
17 DISPLAYED_NODEGROUP_FIELDS = ('uuid', 'status', 'name', 'cluster_name')
18@@ -180,7 +182,17 @@
19 # send it from now on.
20 update_nodegroup_maas_url(nodegroup, request)
21 else:
22- nodegroup = register_nodegroup(request, uuid)
23+ # An RPC connection to the target cluster is required for
24+ # registration to work, so we wait up to 10 seconds for a
25+ # connection to be established.
26+ try:
27+ getClientFor(uuid, timeout=10)
28+ except NoConnectionsAvailable:
29+ return HttpResponse(
30+ "Waiting for RPC connection.",
31+ status=httplib.SERVICE_UNAVAILABLE)
32+ else:
33+ nodegroup = register_nodegroup(request, uuid)
34
35 return compose_nodegroup_register_response(nodegroup, already_existed)
36
37
38=== modified file 'src/maasserver/api/tests/test_register.py'
39--- src/maasserver/api/tests/test_register.py 2014-09-24 14:20:51 +0000
40+++ src/maasserver/api/tests/test_register.py 2014-10-02 01:12:08 +0000
41@@ -42,7 +42,11 @@
42 ANY,
43 Mock,
44 )
45-from testtools.matchers import MatchesStructure
46+from provisioningserver.rpc.exceptions import NoConnectionsAvailable
47+from testtools.matchers import (
48+ Equals,
49+ MatchesStructure,
50+ )
51 from testtools.testcase import ExpectedException
52
53
54@@ -210,6 +214,11 @@
55 This method can be called anonymously.
56 """
57
58+ def setUp(self):
59+ super(TestRegisterAPI, self).setUp()
60+ self.getClientFor = self.patch_autospec(
61+ nodegroups_module, "getClientFor")
62+
63 def test_register_creates_nodegroup_and_interfaces(self):
64 create_configured_master()
65 name = factory.make_name('cluster')
66@@ -443,3 +452,14 @@
67 self.assertEqual([], update_maas_url.call_args_list)
68 # The master's maas_url field remains empty.
69 self.assertEqual("", master.maas_url)
70+
71+ def test_register_nodegroup_when_rpc_not_connected(self):
72+ create_configured_master()
73+ self.getClientFor.side_effect = NoConnectionsAvailable
74+ response = self.client.post(
75+ reverse('nodegroups_handler'),
76+ {'op': 'register', 'uuid': factory.make_UUID()})
77+ self.expectThat(
78+ response.status_code, Equals(httplib.SERVICE_UNAVAILABLE))
79+ self.expectThat(
80+ response.content, Equals("Waiting for RPC connection."))
81
82=== modified file 'src/provisioningserver/start_cluster_controller.py'
83--- src/provisioningserver/start_cluster_controller.py 2014-09-23 20:16:54 +0000
84+++ src/provisioningserver/start_cluster_controller.py 2014-10-02 01:12:08 +0000
85@@ -72,7 +72,12 @@
86 :raise ClusterControllerRejected: if this system has been rejected as a
87 cluster controller.
88 """
89- known_responses = {httplib.OK, httplib.FORBIDDEN, httplib.ACCEPTED}
90+ known_responses = {
91+ httplib.ACCEPTED,
92+ httplib.FORBIDDEN,
93+ httplib.OK,
94+ httplib.SERVICE_UNAVAILABLE,
95+ }
96
97 interfaces = json.dumps(discover_networks())
98 client = make_anonymous_api_client(server_url)
99@@ -100,6 +105,9 @@
100 elif status_code == httplib.ACCEPTED:
101 # Our application is still waiting for approval. Keep trying.
102 return None
103+ elif status_code == httplib.SERVICE_UNAVAILABLE:
104+ # Our cluster has not yet established RPC connections.
105+ return None
106 elif status_code == httplib.FORBIDDEN:
107 # Our application has been rejected. Give up.
108 raise ClusterControllerRejected(
109
110=== modified file 'src/provisioningserver/tests/test_start_cluster_controller.py'
111--- src/provisioningserver/tests/test_start_cluster_controller.py 2014-09-23 20:21:50 +0000
112+++ src/provisioningserver/tests/test_start_cluster_controller.py 2014-10-02 01:12:08 +0000
113@@ -123,6 +123,10 @@
114 """Prepare to return "request pending" from API request."""
115 self.prepare_response(httplib.ACCEPTED)
116
117+ def prepare_rpc_wait_response(self):
118+ """Prepare to return "waiting for RPC" from API request."""
119+ self.prepare_response(httplib.SERVICE_UNAVAILABLE)
120+
121 def test_run_command(self):
122 # We can't really run the script, but we can verify that (with
123 # the right system functions patched out) we can run it
124@@ -154,6 +158,12 @@
125 Sleeping,
126 start_cluster_controller.run, make_args())
127
128+ def test_polls_on_rpc_wait(self):
129+ self.prepare_rpc_wait_response()
130+ self.assertRaises(
131+ Sleeping,
132+ start_cluster_controller.run, make_args())
133+
134 def test_polls_on_unexpected_errors(self):
135 self.patch(MAASDispatcher, 'dispatch_query').side_effect = HTTPError(
136 make_url(), httplib.REQUEST_TIMEOUT, "Timeout.", '', BytesIO())