Merge ~ack/maas:1922569-discover-projects-rpc-error into maas:master

Proposed by Alberto Donato
Status: Merged
Approved by: Alberto Donato
Approved revision: e9d1d5e52c1c537c7708a43783d8b71d15c0b844
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ack/maas:1922569-discover-projects-rpc-error
Merge into: maas:master
Diff against target: 203 lines (+59/-51)
4 files modified
src/provisioningserver/drivers/pod/lxd.py (+5/-2)
src/provisioningserver/drivers/pod/tests/test_lxd.py (+15/-0)
src/provisioningserver/rpc/pods.py (+30/-49)
src/provisioningserver/rpc/tests/test_pods.py (+9/-0)
Reviewer Review Type Date Requested Status
Adam Collard (community) Approve
MAAS Lander Approve
Review via email: mp+400695@code.launchpad.net

Commit message

LP: #1922569 - wrap errors from discover projects call in RPC handler

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

UNIT TESTS
-b 1922569-discover-projects-rpc-error lp:~ack/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas/job/branch-tester/9691/console
COMMIT: e9d1d5e52c1c537c7708a43783d8b71d15c0b844

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

jenkins: !test

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b 1922569-discover-projects-rpc-error lp:~ack/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: e9d1d5e52c1c537c7708a43783d8b71d15c0b844

review: Approve
Revision history for this message
Adam Collard (adam-collard) :
review: Approve

There was an error fetching revisions from git servers. Please try again in a few minutes. If the problem persists, contact Launchpad support.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/src/provisioningserver/drivers/pod/lxd.py b/src/provisioningserver/drivers/pod/lxd.py
index 3683fe2..4095e85 100644
--- a/src/provisioningserver/drivers/pod/lxd.py
+++ b/src/provisioningserver/drivers/pod/lxd.py
@@ -11,7 +11,7 @@ from urllib.parse import urlparse
11import uuid11import uuid
1212
13from pylxd import Client13from pylxd import Client
14from pylxd.exceptions import ClientConnectionFailed, NotFound14from pylxd.exceptions import ClientConnectionFailed, LXDAPIException, NotFound
15import urllib315import urllib3
1616
17from provisioningserver.drivers import (17from provisioningserver.drivers import (
@@ -757,7 +757,10 @@ class LXDPodDriver(PodDriver):
757 if not client.trusted:757 if not client.trusted:
758 password = context.get("password")758 password = context.get("password")
759 if password:759 if password:
760 client.authenticate(password)760 try:
761 client.authenticate(password)
762 except LXDAPIException as e:
763 raise LXDPodError(f"Authentication failed: {e}")
761 else:764 else:
762 raise LXDPodError(765 raise LXDPodError(
763 f"Pod {pod_id}: Certificate is not trusted and no password was given."766 f"Pod {pod_id}: Certificate is not trusted and no password was given."
diff --git a/src/provisioningserver/drivers/pod/tests/test_lxd.py b/src/provisioningserver/drivers/pod/tests/test_lxd.py
index 0976151..0b41f13 100644
--- a/src/provisioningserver/drivers/pod/tests/test_lxd.py
+++ b/src/provisioningserver/drivers/pod/tests/test_lxd.py
@@ -5,6 +5,7 @@ from os.path import join
5import random5import random
6from unittest.mock import ANY, Mock, PropertyMock, sentinel6from unittest.mock import ANY, Mock, PropertyMock, sentinel
77
8from pylxd.exceptions import LXDAPIException
8from testtools.matchers import Equals, IsInstance, MatchesAll, MatchesStructure9from testtools.matchers import Equals, IsInstance, MatchesAll, MatchesStructure
9from testtools.testcase import ExpectedException10from testtools.testcase import ExpectedException
10from twisted.internet.defer import inlineCallbacks11from twisted.internet.defer import inlineCallbacks
@@ -237,6 +238,20 @@ class TestLXDPodDriver(MAASTestCase):
237 with ExpectedException(lxd_module.LXDPodError, error_msg):238 with ExpectedException(lxd_module.LXDPodError, error_msg):
238 driver._get_client(pod_id, context)239 driver._get_client(pod_id, context)
239240
241 def test_get_client_raises_error_when_authenticate_fails(self):
242 context = self.make_parameters_context()
243 pod_id = factory.make_name("pod_id")
244 Client = self.patch(lxd_module, "Client")
245 client = Client.return_value
246 client.trusted = False
247 mock_response = Mock(status_code=403)
248 mock_response.json.return_value = {"error": "auth failed"}
249 client.authenticate.side_effect = LXDAPIException(mock_response)
250 driver = lxd_module.LXDPodDriver()
251 error_msg = "Authentication failed: auth failed"
252 with ExpectedException(lxd_module.LXDPodError, error_msg):
253 driver._get_client(pod_id, context)
254
240 def test_get_machine(self):255 def test_get_machine(self):
241 context = self.make_parameters_context()256 context = self.make_parameters_context()
242 driver = lxd_module.LXDPodDriver()257 driver = lxd_module.LXDPodDriver()
diff --git a/src/provisioningserver/rpc/pods.py b/src/provisioningserver/rpc/pods.py
index bbc422e..6ac985c 100644
--- a/src/provisioningserver/rpc/pods.py
+++ b/src/provisioningserver/rpc/pods.py
@@ -52,6 +52,9 @@ def discover_pod_projects(pod_type, context):
52 }52 }
5353
54 d.addCallback(convert)54 d.addCallback(convert)
55 d.addErrback(
56 convert_errors, log_message="Failed to discover VM host projects."
57 )
55 return d58 return d
5659
5760
@@ -86,18 +89,8 @@ def discover_pod(pod_type, context, pod_id=None, name=None):
86 else:89 else:
87 return {"pod": result}90 return {"pod": result}
8891
89 def catch_all(failure):
90 """Convert all failures into `PodActionFail` unless already a
91 `PodActionFail` or `NotImplementedError`."""
92 # Log locally to help debugging.
93 log.err(failure, "Failed to discover pod.")
94 if failure.check(NotImplementedError, PodActionFail):
95 return failure
96 else:
97 raise PodActionFail(get_error_message(failure.value))
98
99 d.addCallback(convert)92 d.addCallback(convert)
100 d.addErrback(catch_all)93 d.addErrback(convert_errors, log_message="Failed to discover VM host.")
101 return d94 return d
10295
10396
@@ -141,23 +134,12 @@ def compose_machine(pod_type, context, request, pod_id, name):
141 "invalid result." % pod_type134 "invalid result." % pod_type
142 )135 )
143136
144 def catch_all(failure):
145 """Convert all failures into `PodActionFail` unless already a
146 `PodActionFail`, `PodInvalidResources` or `NotImplementedError`."""
147 if failure.check(PodInvalidResources):
148 # Driver returned its own invalid resource exception instead of
149 # None. Just pass this onto the region.
150 return failure
151
152 # Log locally to help debugging.
153 log.err(failure, "%s: Failed to compose machine: %s" % (name, request))
154 if failure.check(NotImplementedError, PodActionFail):
155 return failure
156 else:
157 raise PodActionFail(get_error_message(failure.value))
158
159 d.addCallback(convert)137 d.addCallback(convert)
160 d.addErrback(catch_all)138 d.addErrback(
139 convert_errors,
140 log_message=f"{name}: Failed to compose machine: {request}",
141 keep_failures=[PodInvalidResources],
142 )
161 return d143 return d
162144
163145
@@ -217,23 +199,15 @@ def send_pod_commissioning_results(
217 f"Unable to send Pod commissioning information for {name}({system_id}): {e.error}"199 f"Unable to send Pod commissioning information for {name}({system_id}): {e.error}"
218 )200 )
219201
220 def catch_all(failure):
221 """Convert all failures into `PodActionFail` unless already a
222 `PodActionFail` or `NotImplementedError`."""
223 # Log locally to help debugging.
224 log.err(failure, "Failed to send_pod_commissioning_results.")
225 if failure.check(NotImplementedError, PodActionFail):
226 return failure
227 else:
228 raise PodActionFail(get_error_message(failure.value))
229
230 d.addCallback(202 d.addCallback(
231 lambda commissioning_results: deferToThread(203 lambda commissioning_results: deferToThread(
232 send_items, commissioning_results204 send_items, commissioning_results
233 )205 )
234 )206 )
235 d.addCallback(lambda _: {})207 d.addCallback(lambda _: {})
236 d.addErrback(catch_all)208 d.addErrback(
209 convert_errors, log_message="Failed to send_pod_commissioning_results."
210 )
237 return d211 return d
238212
239213
@@ -261,16 +235,23 @@ def decompose_machine(pod_type, context, pod_id, name):
261 else:235 else:
262 return {"hints": result}236 return {"hints": result}
263237
264 def catch_all(failure):
265 """Convert all failures into `PodActionFail` unless already a
266 `PodActionFail` or `NotImplementedError`."""
267 # Log locally to help debugging.
268 log.err(failure, "Failed to decompose machine.")
269 if failure.check(NotImplementedError, PodActionFail):
270 return failure
271 else:
272 raise PodActionFail(get_error_message(failure.value))
273
274 d.addCallback(convert)238 d.addCallback(convert)
275 d.addErrback(catch_all)239 d.addErrback(convert_errors, log_message="Failed to decompose machine.")
276 return d240 return d
241
242
243def convert_errors(failure, log_message=None, keep_failures=None):
244 """Convert all failures into PodActionFail unless already a
245 PodActionFail or NotImplementedError.
246
247 Optionally, also log a failure message.
248 """
249 valid_failures = [NotImplementedError, PodActionFail]
250 if keep_failures:
251 valid_failures.extend(keep_failures)
252 if log_message:
253 log.err(failure, log_message)
254 if failure.check(*valid_failures):
255 return failure
256 else:
257 raise PodActionFail(get_error_message(failure.value))
diff --git a/src/provisioningserver/rpc/tests/test_pods.py b/src/provisioningserver/rpc/tests/test_pods.py
index aa8f547..0f8bb01 100644
--- a/src/provisioningserver/rpc/tests/test_pods.py
+++ b/src/provisioningserver/rpc/tests/test_pods.py
@@ -41,6 +41,15 @@ class TestDiscoverPodProjects(MAASTestCase):
41 yield pods.discover_pod_projects(unknown_type, {})41 yield pods.discover_pod_projects(unknown_type, {})
4242
43 @inlineCallbacks43 @inlineCallbacks
44 def test_converts_exceptions(self):
45 fake_driver = MagicMock()
46 fake_driver.name = factory.make_name("pod")
47 fake_driver.discover_projects.return_value = fail(Exception("fail!"))
48 self.patch(PodDriverRegistry, "get_item").return_value = fake_driver
49 with ExpectedException(exceptions.PodActionFail):
50 yield pods.discover_pod_projects(fake_driver.name, {})
51
52 @inlineCallbacks
44 def test_return_projects(self):53 def test_return_projects(self):
45 fake_driver = MagicMock()54 fake_driver = MagicMock()
46 fake_driver.name = factory.make_name("pod")55 fake_driver.name = factory.make_name("pod")

Subscribers

People subscribed via source and target branches