Skip to content

Commit 94b6f3d

Browse files
authored
Merge pull request #798 from cderici/base-argument-for-model-deploy
#798 #### Description This adds the `base` argument to the `model.Deploy()`, in accordance with the upcoming Juju `3.1`. #### QA Steps Adds a unit test for the new `parse_base_arg` utility and an integration test for deploying with base, so the following tests should be passing: ``` tox -e py3 -- tests/unit/test_utils.py::TestBaseArgument::test_parse_base_arg ``` ``` tox -e integration -- tests/integration/test_model.py::test_deploy_with_base ```
2 parents 280f76d + fbc4974 commit 94b6f3d

4 files changed

Lines changed: 45 additions & 13 deletions

File tree

juju/model.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,7 +1604,7 @@ async def debug_log(
16041604
async def deploy(
16051605
self, entity_url, application_name=None, bind=None,
16061606
channel=None, config=None, constraints=None, force=False,
1607-
num_units=1, overlays=[], plan=None, resources=None, series=None,
1607+
num_units=1, overlays=[], base=None, resources=None, series=None,
16081608
storage=None, to=None, devices=None, trust=False, attach_storage=[]):
16091609
"""Deploy a new service or bundle.
16101610
@@ -1620,9 +1620,9 @@ async def deploy(
16201620
an unsupported series
16211621
:param int num_units: Number of units to deploy
16221622
:param [] overlays: Bundles to overlay on the primary bundle, applied in order
1623-
:param str plan: Plan under which to deploy charm
1623+
:param str base: The base on which to deploy
16241624
:param dict resources: <resource name>:<file path> pairs
1625-
:param str series: Series on which to deploy
1625+
:param str series: Series on which to deploy DEPRECATED: use --base (with Juju 3.1)
16261626
:param dict storage: Storage constraints TODO how do these look?
16271627
:param to: Placement directive as a string. For example:
16281628
@@ -1675,10 +1675,13 @@ async def deploy(
16751675
identifier = res.identifier
16761676

16771677
charm_series = series
1678+
charm_origin = res.origin
1679+
if base:
1680+
charm_origin.base = utils.parse_base_arg(base)
16781681

16791682
if res.is_bundle:
16801683
handler = BundleHandler(self, trusted=trust, forced=force)
1681-
await handler.fetch_plan(url, res.origin, overlays=overlays)
1684+
await handler.fetch_plan(url, charm_origin, overlays=overlays)
16821685
await handler.execute_plan()
16831686
extant_apps = {app for app in self.applications}
16841687
pending_apps = handler.applications - extant_apps
@@ -1698,11 +1701,12 @@ async def deploy(
16981701
# XXX: we're dropping local resources here, but we don't
16991702
# actually support them yet anyway
17001703
if not res.is_local:
1701-
add_charm_res = await self._add_charm(identifier, res.origin)
1704+
add_charm_res = await self._add_charm(identifier, charm_origin)
17021705
if isinstance(add_charm_res, dict):
17031706
# This is for backwards compatibility for older
17041707
# versions where AddCharm returns a dictionary
1705-
charm_origin = add_charm_res.get('charm_origin', res.origin)
1708+
charm_origin = add_charm_res.get('charm_origin',
1709+
charm_origin)
17061710
else:
17071711
charm_origin = add_charm_res.charm_origin
17081712
if Schema.CHARM_HUB.matches(url.schema):
@@ -1718,15 +1722,13 @@ async def deploy(
17181722
else:
17191723
# We have a local charm dir that needs to be uploaded
17201724
charm_dir = os.path.abspath(os.path.expanduser(identifier))
1721-
charm_origin = res.origin
1722-
base = None
1723-
17241725
metadata = utils.get_local_charm_metadata(charm_dir)
17251726
charm_series = charm_series or await get_charm_series(metadata,
17261727
self)
1727-
base = utils.get_local_charm_base(
1728-
charm_series, channel, metadata, charm_dir, client.Base)
1729-
charm_origin.base = base
1728+
if not base:
1729+
charm_origin.base = utils.get_local_charm_base(
1730+
charm_series, channel, metadata, charm_dir, client.Base)
1731+
17301732
if not application_name:
17311733
application_name = metadata['name']
17321734
if not application_name:

juju/utils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import zipfile
1111

1212
from . import jasyncio, origin, errors
13+
from .client import client
1314

1415

1516
async def execute_process(*cmd, log=None):
@@ -403,3 +404,15 @@ def base_channel_to_series(channel):
403404
:return: str series (e.g. focal)
404405
"""
405406
return get_version_series(origin.Channel.parse(channel).track)
407+
408+
409+
def parse_base_arg(base):
410+
"""Parses a given base into a Client.Base object
411+
:param base str : The base to deploy a charm (e.g. ubuntu@22.04)
412+
"""
413+
client.CharmBase()
414+
if type(base) != str or "@" not in base:
415+
raise errors.JujuError("expected base string to contain os and channel separated by '@'")
416+
417+
name, channel = base.split('@')
418+
return client.Base(name=name, channel=channel)

tests/integration/test_model.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,14 @@ async def test_deploy_from_ch_with_invalid_series(event_loop):
336336
pass
337337

338338

339+
@base.bootstrapped
340+
@pytest.mark.asyncio
341+
async def test_deploy_with_base(event_loop):
342+
async with base.CleanModel() as model:
343+
await model.deploy("ubuntu", base="ubuntu@22.04")
344+
await model.wait_for_idle(status='active')
345+
346+
339347
@base.bootstrapped
340348
@pytest.mark.asyncio
341349
async def test_add_machine(event_loop):

tests/unit/test_utils.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import unittest
22

3-
from juju.utils import juju_config_dir, juju_ssh_key_paths
3+
from juju.utils import juju_config_dir, juju_ssh_key_paths, parse_base_arg
4+
from juju.client import client
45

56

67
class TestDirResolve(unittest.TestCase):
@@ -12,3 +13,11 @@ def test_juju_ssh_key_paths(self):
1213
public, private = juju_ssh_key_paths()
1314
assert public.endswith('ssh/juju_id_rsa.pub')
1415
assert private.endswith('ssh/juju_id_rsa')
16+
17+
18+
class TestBaseArgument(unittest.TestCase):
19+
def test_parse_base_arg(self):
20+
base = parse_base_arg('ubuntu@22.04')
21+
assert isinstance(base, client.Base)
22+
assert base.name == 'ubuntu'
23+
assert base.channel == '22.04'

0 commit comments

Comments
 (0)