From: Pete Vander Giessen Date: Tue, 7 Mar 2017 21:20:39 +0000 (-0600) Subject: Merge branch 'master' into bug/fix-invalid-annotations X-Git-Tag: 0.4.0~15^2 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=27dfe759bbb54ca7dc963c30f6c52406c456983a;hp=a09dd5038d226fbcda54a79bdddc995054857198;p=osm%2FN2VC.git Merge branch 'master' into bug/fix-invalid-annotations --- diff --git a/juju/application.py b/juju/application.py index 9863d88..6b1f8ab 100644 --- a/juju/application.py +++ b/juju/application.py @@ -253,7 +253,7 @@ class Application(model.ModelEntity): self.ann_facade.connect(self.connection) ann = client.EntityAnnotations( - entity=self.name, + entity=self.tag, annotations=annotations, ) return await self.ann_facade.Set([ann]) diff --git a/juju/client/connection.py b/juju/client/connection.py index 7119292..b508a1a 100644 --- a/juju/client/connection.py +++ b/juju/client/connection.py @@ -96,8 +96,30 @@ class Connection: outgoing = json.dumps(msg, indent=2, cls=encoder) await self.ws.send(outgoing) result = await self.recv() - if result and 'error' in result: + + if not result: + return result + + if 'error' in result: + # API Error Response raise JujuAPIError(result) + + if not 'response' in result: + # This may never happen + return result + + if 'results' in result['response']: + # Check for errors in a result list. + errors = [] + for res in result['response']['results']: + if res.get('error', {}).get('message'): + errors.append(res['error']['message']) + if errors: + raise JujuError(errors) + + elif result['response'].get('error', {}).get('message'): + raise JujuError(result['response']['error']['message']) + return result def http_headers(self): diff --git a/juju/model.py b/juju/model.py index 04fb2d4..55ad086 100644 --- a/juju/model.py +++ b/juju/model.py @@ -1565,9 +1565,6 @@ class BundleHandler(object): self.plan = await self.client_facade.GetBundleChanges( yaml.dump(self.bundle)) - if self.plan.errors: - raise JujuError('\n'.join(self.plan.errors)) - async def execute_plan(self): for step in self.plan.changes: method = getattr(self, step.method) diff --git a/tests/integration/test_errors.py b/tests/integration/test_errors.py new file mode 100644 index 0000000..d173bce --- /dev/null +++ b/tests/integration/test_errors.py @@ -0,0 +1,70 @@ +import pytest + +from .. import base + +MB = 1 +GB = 1024 + + +@base.bootstrapped +@pytest.mark.asyncio +async def test_juju_api_error(event_loop): + ''' + Verify that we raise a JujuAPIError for responses with an error in + a top level key (for completely invalid requests). + + ''' + from juju.errors import JujuAPIError + + async with base.CleanModel() as model: + with pytest.raises(JujuAPIError): + await model.add_machine(constraints={'mem': 'foo'}) + + +@base.bootstrapped +@pytest.mark.asyncio +async def test_juju_error_in_results_list(event_loop): + ''' + Replicate the code that caused + https://github.com/juju/python-libjuju/issues/67, and verify that + we get a JujuError instead of passing silently by the failure. + + (We don't raise a JujuAPIError, because the request isn't + completely invalid -- it's just passing a tag that doesn't exist.) + + This also verifies that we will raise a JujuError any time there + is an error in one of a list of results. + + ''' + from juju.errors import JujuError + from juju.client import client + + async with base.CleanModel() as model: + ann_facade = client.AnnotationsFacade() + ann_facade.connect(model.connection) + + ann = client.EntityAnnotations( + entity='badtag', + annotations={'gui-x': '1', 'gui-y': '1'}, + ) + with pytest.raises(JujuError): + return await ann_facade.Set([ann]) + + +@base.bootstrapped +@pytest.mark.asyncio +async def test_juju_error_in_result(event_loop): + ''' + Verify that we raise a JujuError when appropraite when we are + looking at a single result coming back. + + ''' + from juju.errors import JujuError + from juju.client import client + + async with base.CleanModel() as model: + app_facade = client.ApplicationFacade() + app_facade.connect(model.connection) + + with pytest.raises(JujuError): + return await app_facade.GetCharmURL('foo')