Skip to content

Commit 36d078b

Browse files
authored
Merge pull request #884 from cderici/no-upgr-resource-if-same-revision
#884 #### Description This adds a condition to the logic of deciding if we need to upgrade a resource during an `app.refresh()`, analogous to the [`shouldUpgradeResource()` utility](https://github.com/juju/juju/blob/cdda0d771219c9e59bfef4579da8b6389e1b0d72/cmd/juju/application/utils/utils.go#L159) in Juju. Fixes #883 #### QA Steps This adds the use case in #883 as an integration test, so the following should pass: ``` tox -e integration -- tests/integration/test_application.py::test_upgrade_charm_resource_same_rev_no_update ``` Also per #884 (comment), this also splits out the resource refresh decider logic into a separate function and adds unit tests for it, so the following should also pass: ```sh tox -e py3 -- tests/unit/test_utils.py::TestShouldUpgradeResource ```
2 parents 7a70f97 + b04014a commit 36d078b

4 files changed

Lines changed: 116 additions & 8 deletions

File tree

juju/application.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -740,11 +740,10 @@ async def refresh(
740740
}
741741

742742
# Compute the difference btw resources needed and the existing resources
743-
resources_to_update = [
744-
resource for resource in charm_resources
745-
if resource.get('Name', resource.get('name')) not in existing_resources or
746-
existing_resources[resource.get('Name', resource.get('name'))].origin != 'upload'
747-
]
743+
resources_to_update = []
744+
for resource in charm_resources:
745+
if utils.should_upgrade_resource(resource, existing_resources):
746+
resources_to_update.append(resource)
748747

749748
# Update the resources
750749
if resources_to_update:

juju/utils.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,3 +389,30 @@ def base_channel_to_series(channel):
389389
:return: str series (e.g. focal)
390390
"""
391391
return get_version_series(origin.Channel.parse(channel).track)
392+
393+
394+
def should_upgrade_resource(available_resource, existing_resources):
395+
"""Called in the context of upgrade_charm. Given a resource R, takes a look at the resources we
396+
already have and decides if we need to refresh R.
397+
398+
:param dict[str] available_resource: The dict representing the client.Resource coming from the
399+
charmhub api. We're considering if we need to refresh this during upgrade_charm.
400+
:param dict[str] existing_resources: The dict coming from resources_facade.ListResources
401+
representing the resources of the currently deployed charm.
402+
403+
:result bool: The decision to refresh the given resource
404+
"""
405+
# should upgrade resource?
406+
res_name = available_resource.get('Name', available_resource.get('name'))
407+
# no, if it's upload
408+
if existing_resources[res_name].origin == 'upload':
409+
return False
410+
411+
# no, if we already have it (and upstream doesn't have a newer res available)
412+
if res_name in existing_resources:
413+
available_rev = available_resource.get('Revision', available_resource.get('revision', -1))
414+
u_fields = existing_resources[res_name].unknown_fields
415+
existing_rev = u_fields.get('Revision', u_fields.get('revision', -1))
416+
if existing_rev >= available_rev:
417+
return False
418+
return True

tests/integration/test_application.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,17 @@ async def test_upgrade_charm_resource(event_loop):
226226
assert app.units[0].workload_status_message == expected_message
227227

228228

229+
@base.bootstrapped
230+
@pytest.mark.asyncio
231+
async def test_upgrade_charm_resource_same_rev_no_update(event_loop):
232+
async with base.CleanModel() as model:
233+
app = await model.deploy('keystone', channel='victoria/stable')
234+
ress = await app.get_resources()
235+
await app.refresh(channel='ussuri/stable')
236+
ress2 = await app.get_resources()
237+
assert ress['policyd-override'].fingerprint == ress2['policyd-override'].fingerprint
238+
239+
229240
@base.bootstrapped
230241
@pytest.mark.asyncio
231242
async def test_trusted(event_loop):

tests/unit/test_utils.py

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,85 @@
11
import unittest
22

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

56

67
class TestDirResolve(unittest.TestCase):
78
def test_config_dir(self):
8-
config_dir = juju_config_dir()
9+
config_dir = utils.juju_config_dir()
910
assert 'local/share/juju' in config_dir
1011

1112
def test_juju_ssh_key_paths(self):
12-
public, private = juju_ssh_key_paths()
13+
public, private = utils.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 TestShouldUpgradeResource(unittest.TestCase):
19+
def test_should_upgrade_resource_no_same_rev(self):
20+
# fields are trimmed for readability
21+
res = {'created-at': '2019-10-24T20:45:19.201000',
22+
'description': 'The policy.d overrides file',
23+
'download': {'hash-sha-256': 'e3b0c4', 'hash-sha-384': '38b060a751ac914898b95b',
24+
'hash-sha-512': 'cf83e1357eef1a538327af927da3e',
25+
'hash-sha3-384': '0c63a75b1bbed1e058d5f004',
26+
'size': 0,
27+
'url': 'https://api.charmhub.io/api/v1/resMGU0L516cGTTwNam.policyd-override_0'},
28+
'filename': 'policyd-override.zip', 'name': 'policyd-override', 'revision': 0, 'type': 'file'}
29+
30+
existing = {
31+
'policyd-override':
32+
client.Resource(charmresource=None,
33+
application='keystone', id_='keystone/policyd-override', pending_id='',
34+
timestamp='0001-01-01T00:00:00Z', username='', name='policyd-override',
35+
origin='store', type='file', path='policyd-override.zip',
36+
description='The policy.doverrides file',
37+
revision=0, fingerprint='OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb',
38+
size=0)}
39+
assert not utils.should_upgrade_resource(res, existing)
40+
41+
def test_should_upgrade_resource_no_local_upload(self):
42+
# fields are trimmed for readability
43+
res = {'created-at': '2019-10-24T20:45:19.201000',
44+
'description': 'The policy.d overrides file',
45+
'download': {'hash-sha-256': 'e3b0c4', 'hash-sha-384': '38b060a751ac914898b95b',
46+
'hash-sha-512': 'cf83e1357eef1a538327af927da3e',
47+
'hash-sha3-384': '0c63a75b1bbed1e058d5f004',
48+
'size': 0,
49+
'url': 'https://api.charmhub.io/api/v1/resMGU0L516cGTTwNam.policyd-override_0'},
50+
'filename': 'policyd-override.zip', 'name': 'local_res', 'revision': 0,
51+
'type': 'file'}
52+
53+
existing = {
54+
'local_res':
55+
client.Resource(charmresource=None,
56+
application='keystone', id_='keystone/policyd-override', pending_id='',
57+
timestamp='0001-01-01T00:00:00Z', username='', name='policyd-override',
58+
origin='upload', type='file', path='policyd-override.zip',
59+
description='The policy.doverrides file',
60+
revision=0, fingerprint='OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb',
61+
size=0)}
62+
assert not utils.should_upgrade_resource(res, existing)
63+
64+
def test_should_upgrade_resource_yes_new_revision(self):
65+
# fields are trimmed for readability
66+
res = {'created-at': '2019-10-24T20:45:19.201000',
67+
'description': 'The policy.d overrides file',
68+
'download': {'hash-sha-256': 'e3b0c4', 'hash-sha-384': '38b060a751ac914898b95b',
69+
'hash-sha-512': 'cf83e1357eef1a538327af927da3e',
70+
'hash-sha3-384': '0c63a75b1bbed1e058d5f004',
71+
'size': 0,
72+
'url': 'https://api.charmhub.io/api/v1/resMGU0L516cGTTwNam.policyd-override_0'},
73+
'filename': 'policyd-override.zip', 'name': 'policyd-override', 'revision': 1,
74+
'type': 'file'}
75+
76+
existing = {
77+
'policyd-override':
78+
client.Resource(charmresource=None,
79+
application='keystone', id_='keystone/policyd-override', pending_id='',
80+
timestamp='0001-01-01T00:00:00Z', username='', name='policyd-override',
81+
origin='store', type='file', path='policyd-override.zip',
82+
description='The policy.doverrides file',
83+
revision=0, fingerprint='OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb',
84+
size=0)}
85+
assert utils.should_upgrade_resource(res, existing)

0 commit comments

Comments
 (0)