diff --git a/juju/bundle.py b/juju/bundle.py index e8eee5731..12239c881 100644 --- a/juju/bundle.py +++ b/juju/bundle.py @@ -659,6 +659,7 @@ class AddCharmChange(ChangeInfo): _toPy = {'charm': 'charm', 'series': 'series', 'channel': 'channel', + 'revision': 'revision', 'architecture': 'architecture'} """AddCharmChange holds a change for adding a charm to the environment. @@ -675,6 +676,7 @@ class AddCharmChange(ChangeInfo): :series: series of the charm to be added if the charm default is not sufficient. :channel: preferred channel for obtaining the charm. + :revision: specified revision of the charm to be added if specified. """ @staticmethod def method(): @@ -710,6 +712,7 @@ async def run(self, context): architecture=arch, risk=ch.risk, track=ch.track, + revision=self.revision, base=base) identifier, origin = await context.model._resolve_charm(url, origin) diff --git a/tests/integration/bundle/bundle-with-charm-revision.yaml b/tests/integration/bundle/bundle-with-charm-revision.yaml new file mode 100644 index 000000000..fbd8c1574 --- /dev/null +++ b/tests/integration/bundle/bundle-with-charm-revision.yaml @@ -0,0 +1,6 @@ +applications: + hello-juju: + charm: "hello-juju" + channel: latest/stable + revision: 7 + num_units: 1 diff --git a/tests/integration/test_model.py b/tests/integration/test_model.py index a2165e8fd..b078cc8db 100644 --- a/tests/integration/test_model.py +++ b/tests/integration/test_model.py @@ -15,6 +15,7 @@ import pytest from juju import jasyncio, tag, url from juju.client import client +from juju.client._definitions import FullStatus from juju.errors import JujuError, JujuModelError, JujuUnitError, JujuConnectionError from juju.model import Model, ModelObserver from juju.utils import block_until, run_with_interrupt, wait_for_bundle, base_channel_to_series @@ -181,6 +182,25 @@ async def test_deploy_bundle_local_charm_series_manifest(): assert model.units['test1/0'].workload_status == 'active' +@base.bootstrapped +@pytest.mark.bundle +async def test_deploy_bundle_with_pinned_charm_revision(): + bundle_dir = INTEGRATION_TEST_DIR / 'bundle' + bundle_yaml_path = bundle_dir / 'bundle-with-charm-revision.yaml' + # Revision of the hello-juju charm defined in the bundle yaml + # We can also read the yaml to get the revision but we are hard-coding it for now for simplicity + pinned_revision = 7 + + async with base.CleanModel() as model: + await model.deploy(str(bundle_yaml_path)) + + application = model.applications.get('hello-juju', None) + status: FullStatus = await model.get_status([application.name]) + # the 'charm' field of application status should be of this format: + # ch:amd64/{series}/{name}-{revision} + assert f"{application.name}-{pinned_revision}" in status.applications[application.name]["charm"] + + @base.bootstrapped @pytest.mark.bundle async def test_deploy_invalid_bundle(): diff --git a/tests/unit/test_bundle.py b/tests/unit/test_bundle.py index 22ea15877..5140dfd36 100644 --- a/tests/unit/test_bundle.py +++ b/tests/unit/test_bundle.py @@ -312,12 +312,14 @@ def test_dict_params(self): change = AddCharmChange(1, [], params={"charm": "charm", "series": "series", "channel": "channel", + "revision": "revision", "architecture": "architecture"}) self.assertEqual({"change_id": 1, "requires": [], "charm": "charm", "series": "series", "channel": "channel", + "revision": "revision", "architecture": "architecture"}, change.__dict__) def test_dict_params_missing_data(self): @@ -328,6 +330,7 @@ def test_dict_params_missing_data(self): "charm": "charm", "series": "series", "channel": None, + "revision": None, "architecture": None}, change.__dict__)