From 8853ffc9d7f624099e476a740ca1eab2393f990a Mon Sep 17 00:00:00 2001 From: Cynerva Date: Fri, 2 Jun 2017 09:53:28 -0500 Subject: [PATCH] Implement Application.upgrade_charm method (#132) * Implement Application.upgrade_charm method * Use model.block_until instead of a direct loop on asyncio.sleep * Raise JujuError if upgrade_charm is a noop Same behavior as Juju CLI here. * Add integration tests for upgrade_charm * Fix upgrade_charm hangs forever if switch arg does not start with 'cs:' --- juju/application.py | 55 +++++++++++++++++++++++++-- tests/integration/test_application.py | 42 ++++++++++++++++++++ 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/juju/application.py b/juju/application.py index 2733a6c..8719a62 100644 --- a/juju/application.py +++ b/juju/application.py @@ -3,6 +3,7 @@ import logging from . import model from .client import client +from .errors import JujuError from .placement import parse as parse_placement log = logging.getLogger(__name__) @@ -314,9 +315,9 @@ class Application(model.ModelEntity): """ raise NotImplementedError() - def upgrade_charm( + async def upgrade_charm( self, channel=None, force_series=False, force_units=False, - path=None, resources=None, revision=-1, switch=None): + path=None, resources=None, revision=None, switch=None): """Upgrade the charm for this application. :param str channel: Channel to use when getting the charm from the @@ -331,7 +332,55 @@ class Application(model.ModelEntity): :param str switch: Crossgrade charm url """ - raise NotImplementedError() + # TODO: Support local upgrades + if path is not None: + raise NotImplementedError("path option is not implemented") + if resources is not None: + raise NotImplementedError("resources option is not implemented") + + if switch is not None and revision is not None: + raise ValueError("switch and revision are mutually exclusive") + + client_facade = client.ClientFacade.from_connection(self.connection) + app_facade = client.ApplicationFacade.from_connection(self.connection) + + if switch is not None: + charm_url = switch + if not charm_url.startswith('cs:'): + charm_url = 'cs:' + charm_url + else: + charm_url = self.data['charm-url'] + charm_url = charm_url.rpartition('-')[0] + if revision is not None: + charm_url = "%s-%d" % (charm_url, revision) + else: + charmstore = self.model.charmstore + entity = await charmstore.entity(charm_url, channel=channel) + charm_url = entity['Id'] + + if charm_url == self.data['charm-url']: + raise JujuError('already running charm "%s"' % charm_url) + + await client_facade.AddCharm( + url=charm_url, + channel=channel + ) + + await app_facade.SetCharm( + application=self.entity_id, + channel=channel, + charm_url=charm_url, + config_settings=None, + config_settings_yaml=None, + force_series=force_series, + force_units=force_units, + resource_ids=None, + storage_constraints=None + ) + + await self.model.block_until( + lambda: self.data['charm-url'] == charm_url + ) async def get_metrics(self): """Get metrics for this application's units. diff --git a/tests/integration/test_application.py b/tests/integration/test_application.py index 1618a5a..1a4fcaa 100644 --- a/tests/integration/test_application.py +++ b/tests/integration/test_application.py @@ -50,3 +50,45 @@ async def test_add_units(event_loop): assert len(units) == 2 for unit in units: assert isinstance(unit, Unit) + + +@base.bootstrapped +@pytest.mark.asyncio +async def test_upgrade_charm(event_loop): + async with base.CleanModel() as model: + app = await model.deploy('ubuntu-0') + assert app.data['charm-url'] == 'cs:ubuntu-0' + await app.upgrade_charm() + assert app.data['charm-url'].startswith('cs:ubuntu-') + assert app.data['charm-url'] != 'cs:ubuntu-0' + + +@base.bootstrapped +@pytest.mark.asyncio +async def test_upgrade_charm_channel(event_loop): + async with base.CleanModel() as model: + app = await model.deploy('ubuntu-0') + assert app.data['charm-url'] == 'cs:ubuntu-0' + await app.upgrade_charm(channel='stable') + assert app.data['charm-url'].startswith('cs:ubuntu-') + assert app.data['charm-url'] != 'cs:ubuntu-0' + + +@base.bootstrapped +@pytest.mark.asyncio +async def test_upgrade_charm_revision(event_loop): + async with base.CleanModel() as model: + app = await model.deploy('ubuntu-0') + assert app.data['charm-url'] == 'cs:ubuntu-0' + await app.upgrade_charm(revision=8) + assert app.data['charm-url'] == 'cs:ubuntu-8' + + +@base.bootstrapped +@pytest.mark.asyncio +async def test_upgrade_charm_switch(event_loop): + async with base.CleanModel() as model: + app = await model.deploy('ubuntu-0') + assert app.data['charm-url'] == 'cs:ubuntu-0' + await app.upgrade_charm(switch='ubuntu-8') + assert app.data['charm-url'] == 'cs:ubuntu-8' -- 2.25.1