Skip to content

Commit d2c1cae

Browse files
authored
Merge pull request #973 from cderici/app-refresh-with-resources-on-3.x
#973 #### Description This is a forward port of #960, bringing support for refreshing application with providing `resources` as arguments. Fixes #955 and #883 for 3.x track. #### QA Steps QA steps should be the same with #960: So the fix for the #955 can be tested manually by recreating the scenario above with pylibjuju as follows: ```python $ python -m asyncio >>> import asyncio >>> from juju import model;m=model.Model();await m.connect() >>> await m.deploy('ceph-mon', channel='octopus/stable') ``` Check the resources for that using juju-cli: ```sh $ juju resources ceph-mon No resources to display. ``` Go back to the python repl: ```python >>> await m.applications['ceph-mon'].refresh(channel='quincy/stable') This should succeed ``` And check the resources again external to pylibjuju, you should see the resource is added: ```sh $ juju resources ceph-juju Resource Supplied by Revision alert-rules charmstore 1 ``` Additionally, an integration test for the second part of the PR that adds the resource argument to refresh is added, so just run that, and maybe play with it manually for different charms: ``` tox -e integration -- tests/integration/test_application.py::test_refresh_with_resource_argument ``` All CI tests need to pass. #### Notes & Discussion JUJU-4736
2 parents 9e0e39d + f34b91d commit d2c1cae

3 files changed

Lines changed: 63 additions & 10 deletions

File tree

juju/application.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -686,9 +686,6 @@ async def refresh(
686686
force_units, path or switch, resources)
687687
return
688688

689-
if resources is not None:
690-
raise NotImplementedError("resources option is not implemented")
691-
692689
# If switch is not None at this point, that means it's a switch to a store charm
693690
charm_url = switch or charm_url_origin_result.url
694691
parsed_url = URL.parse(charm_url)
@@ -736,6 +733,22 @@ async def refresh(
736733
err = charm_origin_result.error
737734
raise JujuError(f'{err.code} : {err.message}')
738735

736+
# Now take care of the resources:
737+
738+
# user supplied resources to be used in refresh,
739+
# will override the default values if there's any
740+
arg_resources = resources or {}
741+
742+
# need to process the given resources, as they can be
743+
# paths or revisions
744+
_arg_res_filenames = {}
745+
_arg_res_revisions = {}
746+
for res, filename_or_rev in arg_resources.items():
747+
if isinstance(filename_or_rev, int):
748+
_arg_res_revisions[res] = filename_or_rev
749+
else:
750+
_arg_res_filenames[res] = filename_or_rev
751+
739752
# Already prepped the charm_resources
740753
# Now get the existing resources from the ResourcesFacade
741754
request_data = [client.Entity(self.tag)]
@@ -749,23 +762,25 @@ async def refresh(
749762
# Compute the difference btw resources needed and the existing resources
750763
resources_to_update = []
751764
for resource in charm_resources:
752-
if utils.should_upgrade_resource(resource, existing_resources):
765+
if utils.should_upgrade_resource(resource, existing_resources, arg_resources):
753766
resources_to_update.append(resource)
754767

755768
# Update the resources
756769
if resources_to_update:
757770
request_data = []
758771
for resource in resources_to_update:
772+
res_name = resource.get('Name', resource.get('name'))
759773
request_data.append(client.CharmResource(
760774
description=resource.get('Description', resource.get('description')),
761-
fingerprint=resource.get('Fingerprint', resource.get('fingerprint')),
762-
name=resource.get('Name', resource.get('name')),
763-
path=resource.get('Path', resource.get('filename')),
764-
revision=resource.get('Revision', resource.get('revision', -1)),
765-
size=resource.get('Size', resource.get('size')),
775+
name=res_name,
776+
path=_arg_res_filenames.get(res_name,
777+
resource.get('Path',
778+
resource.get('filename', ''))),
779+
revision=_arg_res_revisions.get(res_name, -1),
766780
type_=resource.get('Type', resource.get('type')),
767781
origin='store',
768782
))
783+
769784
response = await resources_facade.AddPendingResources(
770785
application_tag=self.tag,
771786
charm_url=charm_url,

juju/utils.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,20 +532,25 @@ def series_selector(series_arg='', charm_url=None, model_config=None, supported_
532532
return DEFAULT_SUPPORTED_LTS
533533

534534

535-
def should_upgrade_resource(available_resource, existing_resources):
535+
def should_upgrade_resource(available_resource, existing_resources, arg_resources={}):
536536
"""Called in the context of upgrade_charm. Given a resource R, takes a look at the resources we
537537
already have and decides if we need to refresh R.
538538
539539
:param dict[str] available_resource: The dict representing the client.Resource coming from the
540540
charmhub api. We're considering if we need to refresh this during upgrade_charm.
541541
:param dict[str] existing_resources: The dict coming from resources_facade.ListResources
542542
representing the resources of the currently deployed charm.
543+
:param dict[str] arg_resources: user provided resources to be refreshed
543544
544545
:result bool: The decision to refresh the given resource
545546
"""
547+
546548
# should upgrade resource?
547549
res_name = available_resource.get('Name', available_resource.get('name'))
548550

551+
if res_name in arg_resources:
552+
return True
553+
549554
# do we have it already?
550555
if res_name in existing_resources:
551556
# no upgrade, if it's upload

tests/integration/test_application.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,39 @@ async def test_upgrade_local_charm_resource(event_loop):
210210

211211

212212
@base.bootstrapped
213+
@pytest.mark.asyncio
214+
async def test_upgrade_charm_resource(event_loop):
215+
async with base.CleanModel() as model:
216+
app = await model.deploy('cs:~juju-qa/bionic/upgrade-charm-resource-test-0')
217+
218+
await model.wait_for_idle(wait_for_units=1)
219+
unit = app.units[0]
220+
expected_message = 'I have no resource.'
221+
assert unit.workload_status_message == expected_message
222+
223+
await app.upgrade_charm(revision=1)
224+
await model.block_until(
225+
lambda: unit.workload_status_message != 'I have no resource.',
226+
timeout=60,
227+
)
228+
expected_message = 'My resource: I am the resource.'
229+
assert app.units[0].workload_status_message == expected_message
230+
231+
232+
@base.bootstrapped
233+
@pytest.mark.asyncio
234+
async def test_refresh_with_resource_argument(event_loop):
235+
async with base.CleanModel() as model:
236+
app = await model.deploy('juju-qa-test', resources={'foo-file': 2})
237+
res2 = await app.get_resources()
238+
assert res2['foo-file'].revision == 2
239+
await app.refresh(resources={'foo-file': 4})
240+
res4 = await app.get_resources()
241+
assert res4['foo-file'].revision == 4
242+
243+
244+
@base.bootstrapped
245+
@pytest.mark.asyncio
213246
async def test_upgrade_charm_resource_same_rev_no_update(event_loop):
214247
async with base.CleanModel() as model:
215248
app = await model.deploy('keystone', channel='victoria/stable')

0 commit comments

Comments
 (0)