Skip to content

Commit 3eb840e

Browse files
Merge branch 'master' into add-owner-and-data-to-license
2 parents af4321d + 2381818 commit 3eb840e

3 files changed

Lines changed: 66 additions & 9 deletions

File tree

juju/model.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -548,10 +548,16 @@ async def resolve(self, url, architecture, app_name=None, channel=None, series=N
548548
if channel is not None:
549549
ch = Channel.parse(channel).normalize()
550550

551+
base = client.Base()
552+
if series:
553+
base.channel = ch.normalize().compute_base_channel(series=series)
554+
base.name = 'ubuntu'
551555
origin = client.CharmOrigin(source="charm-hub",
552556
architecture=architecture,
553557
risk=ch.risk,
554-
track=ch.track)
558+
track=ch.track,
559+
base=base,
560+
)
555561

556562
charm_url_str, origin, supported_series = await self.charm_resolver(url, origin)
557563
charm_url = URL.parse(charm_url_str)
@@ -1739,8 +1745,12 @@ async def deploy(
17391745
raise JujuError('unknown charm or bundle {}'.format(entity_url))
17401746
identifier = res.identifier
17411747

1742-
series = res.origin.series if self.connection().is_using_old_client \
1743-
else series
1748+
charm_series = series
1749+
1750+
if self.connection().is_using_old_client and charm_series is None:
1751+
# Also try
1752+
charm_series = res.origin.series
1753+
17441754
if res.is_bundle:
17451755
handler = BundleHandler(self, trusted=trust, forced=force)
17461756
await handler.fetch_plan(url, res.origin, overlays=overlays)
@@ -1792,25 +1802,27 @@ async def deploy(
17921802
metadata = utils.get_local_charm_metadata(charm_dir)
17931803
# TODO (cderici) : pass the metadata into get_charm_series, as
17941804
# it also reads that file redundantly
1795-
series = series or await get_charm_series(charm_dir, self)
1805+
charm_series = charm_series or await get_charm_series(charm_dir,
1806+
self)
17961807

17971808
# If we're using a newer client, then the CharmOrigin needs a
17981809
# base
17991810
if not self.connection().is_using_old_client:
1800-
charm_origin.base = utils.get_local_charm_base(series,
1811+
charm_origin.base = utils.get_local_charm_base(charm_series,
18011812
channel,
18021813
metadata,
18031814
charm_dir,
18041815
client.Base)
18051816

18061817
if not application_name:
18071818
application_name = metadata['name']
1808-
if self.connection().is_using_old_client and not series:
1819+
if self.connection().is_using_old_client and not charm_series:
18091820
raise JujuError(
18101821
"Couldn't determine series for charm at {}. "
18111822
"Pass a 'series' kwarg to Model.deploy().".format(
18121823
charm_dir))
1813-
identifier = await self.add_local_charm_dir(charm_dir, series)
1824+
identifier = await self.add_local_charm_dir(charm_dir,
1825+
charm_series)
18141826
resources = await self.add_local_resources(application_name,
18151827
identifier,
18161828
metadata,
@@ -1824,7 +1836,7 @@ async def deploy(
18241836
return await self._deploy(
18251837
charm_url=identifier,
18261838
application=res.app_name,
1827-
series=series,
1839+
series=charm_series,
18281840
config=config,
18291841
constraints=constraints,
18301842
endpoint_bindings=bind,
@@ -1850,12 +1862,28 @@ async def _add_charm(self, charm_url, origin):
18501862
return await client_facade.AddCharm(channel=str(origin.risk), url=charm_url, force=False)
18511863

18521864
async def _resolve_charm(self, url, origin):
1865+
"""Calls Charms.ResolveCharms to resolve all the fields of the
1866+
charm_origin and also the url and the supported_series
1867+
1868+
:param str url: The url of the charm
1869+
:param client.CharmOrigin origin: The manually constructed origin
1870+
based on what we know about the charm and the deployment so far
1871+
1872+
Returns the confirmed origin returned by the Juju API to be used in
1873+
calls like ApplicationFacade.Deploy
1874+
1875+
:returns str, client.CharmOrigin, [str]
1876+
"""
18531877
charms_cls = client.CharmsFacade
18541878
if charms_cls.best_facade_version(self.connection()) < 3:
18551879
raise JujuError("resolve charm")
18561880

18571881
charms_facade = charms_cls.from_connection(self.connection())
18581882

1883+
# TODO (cderici): following part can be refactored out, since the
1884+
# origin should be set (including the base) before calling this,
1885+
# though all tests need to run (in earlier versions too) before
1886+
# committing to make sure there's no regression
18591887
if Schema.CHARM_STORE.matches(url.schema):
18601888
source = "charm-store"
18611889
else:
@@ -1867,9 +1895,9 @@ async def _resolve_charm(self, url, origin):
18671895
'track': origin.track,
18681896
'risk': origin.risk,
18691897
}
1870-
18711898
if not self.connection().is_using_old_client:
18721899
resolve_origin['base'] = origin.base
1900+
18731901
resp = await charms_facade.ResolveCharms(resolve=[{
18741902
'reference': str(url),
18751903
'charm-origin': resolve_origin

juju/origin.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from enum import Enum
22
from .errors import JujuError
33

4+
from . import utils
5+
46

57
class Source(Enum):
68
"""Source defines a origin source. Providing a hint to the controller about
@@ -112,6 +114,19 @@ def __str__(self):
112114
path = "{}/{}".format(self.track, path)
113115
return path
114116

117+
def compute_base_channel(self, series=None):
118+
"""Determines the channel for a client.Base
119+
A base channel is a track/risk/branch
120+
121+
"""
122+
_ch = [self.risk]
123+
tr = self.track
124+
if series:
125+
tr = utils.get_series_version(series)
126+
if tr:
127+
_ch = [tr] + _ch
128+
return "/".join(_ch)
129+
115130

116131
class Platform:
117132
"""ParsePlatform parses a string representing a store platform.

tests/integration/test_unit.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,17 @@ async def test_resolve_local(event_loop):
239239
# Errored units won't get cleaned up unless we force them.
240240
await asyncio.gather(*(machine.destroy(force=True)
241241
for machine in model.machines.values()))
242+
243+
244+
@base.bootstrapped
245+
@pytest.mark.asyncio
246+
async def test_unit_introspect(event_loop):
247+
async with base.CleanModel() as model:
248+
await model.deploy('ubuntu', series='jammy')
249+
await model.wait_for_idle(status="active")
250+
251+
await model.deploy('juju-introspect',
252+
channel='edge',
253+
series='jammy',
254+
to='0',
255+
)

0 commit comments

Comments
 (0)