Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Application refresh with resources #960

Merged
merged 4 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions juju/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,7 @@ async def set_constraints(self, constraints):

async def refresh(
self, channel=None, force=False, force_series=False, force_units=False,
path=None, resources=None, revision=None, switch=None):
path=None, resources={}, revision=None, switch=None):
"""Refresh the charm for this application.

:param str channel: Channel to use when getting the charm from the
Expand All @@ -635,9 +635,6 @@ async def refresh(
:param str switch: Crossgrade charm url

"""
if path is None and resources is not None:
raise NotImplementedError("refreshing a non-local charm with resources option is not yet implemented")

if switch is not None and revision is not None:
raise ValueError("switch and revision parameters are mutually exclusive in application refresh")

Expand Down Expand Up @@ -727,6 +724,20 @@ async def refresh(

# Now take care of the resources:

# user supplied resources to be used in refresh,
# will override the default values if there's any
arg_resources = resources

# need to process the given resources, as they can be
# paths or revisions
_arg_res_filenames = {}
_arg_res_revisions = {}
for res, filename_or_rev in arg_resources.items():
if isinstance(filename_or_rev, int):
_arg_res_revisions[res] = filename_or_rev
else:
_arg_res_filenames[res] = filename_or_rev

# Already prepped the charm_resources
# Now get the existing resources from the ResourcesFacade
request_data = [client.Entity(self.tag)]
Expand All @@ -739,23 +750,25 @@ async def refresh(
# Compute the difference btw resources needed and the existing resources
resources_to_update = []
for resource in charm_resources:
if utils.should_upgrade_resource(resource, existing_resources):
if utils.should_upgrade_resource(resource, existing_resources, arg_resources):
resources_to_update.append(resource)

# Update the resources
if resources_to_update:
request_data = []
for resource in resources_to_update:
res_name = resource.get('Name', resource.get('name'))
request_data.append(client.CharmResource(
description=resource.get('Description', resource.get('description')),
fingerprint=resource.get('Fingerprint', resource.get('fingerprint')),
name=resource.get('Name', resource.get('name')),
path=resource.get('Path', resource.get('filename')),
revision=resource.get('Revision', resource.get('revision', -1)),
size=resource.get('Size', resource.get('size')),
name=res_name,
path=_arg_res_filenames.get(res_name,
resource.get('Path',
resource.get('filename', ''))),
revision=_arg_res_revisions.get(res_name, -1),
type_=resource.get('Type', resource.get('type')),
origin='store',
))

response = await resources_facade.AddPendingResources(
application_tag=self.tag,
charm_url=charm_url,
Expand Down
7 changes: 6 additions & 1 deletion juju/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,20 +394,25 @@ def base_channel_to_series(channel):
return get_version_series(origin.Channel.parse(channel).track)


def should_upgrade_resource(available_resource, existing_resources):
def should_upgrade_resource(available_resource, existing_resources, arg_resources={}):
"""Called in the context of upgrade_charm. Given a resource R, takes a look at the resources we
already have and decides if we need to refresh R.

:param dict[str] available_resource: The dict representing the client.Resource coming from the
charmhub api. We're considering if we need to refresh this during upgrade_charm.
:param dict[str] existing_resources: The dict coming from resources_facade.ListResources
representing the resources of the currently deployed charm.
:param dict[str] arg_resources: user provided resources to be refreshed

:result bool: The decision to refresh the given resource
"""

# should upgrade resource?
res_name = available_resource.get('Name', available_resource.get('name'))

if res_name in arg_resources:
return True

# do we have it already?
if res_name in existing_resources:
# no upgrade, if it's upload
Expand Down
12 changes: 12 additions & 0 deletions tests/integration/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,18 @@ async def test_upgrade_charm_resource(event_loop):
assert app.units[0].workload_status_message == expected_message


@base.bootstrapped
@pytest.mark.asyncio
async def test_refresh_with_resource_argument(event_loop):
async with base.CleanModel() as model:
app = await model.deploy('juju-qa-test', resources={'foo-file': 2})
res2 = await app.get_resources()
assert res2['foo-file'].revision == 2
await app.refresh(resources={'foo-file': 4})
res4 = await app.get_resources()
assert res4['foo-file'].revision == 4


@base.bootstrapped
@pytest.mark.asyncio
async def test_upgrade_charm_resource_same_rev_no_update(event_loop):
Expand Down