Skip to content

Commit fabb5d5

Browse files
authored
Merge pull request #907 from jack-w-shaw/JUJU-4048_use_GetChangesMapArgs
#907 Use GetChangesMapArgs for bundle changes instead of GetChanges Also, drop support for GetChanges when parsing ChangeInfos GetChanges returns a list of params in a magical order. This makes it brittle and adds lots of special knowledge boiler plate. GetChangesMapArgs was added a few years ago to replace it. Wer already support map arg params, we just never do so #### QA Steps ``` tox -e py3 ``` ``` tox -e integration -- -m bundle ```
2 parents 1e7ef53 + 95754c9 commit fabb5d5

4 files changed

Lines changed: 21 additions & 289 deletions

File tree

juju/bundle.py

Lines changed: 5 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from toposort import toposort_flatten
1313

1414
from .client import client
15-
from .constraints import parse as parse_constraints, parse_storage_constraint, parse_device_constraint
15+
from .constraints import parse as parse_constraints
1616
from .errors import JujuError
1717
from . import utils, jasyncio
1818
from .origin import Channel, Source
@@ -288,7 +288,7 @@ async def fetch_plan(self, bundle_url, origin, overlays=[]):
288288
_yaml_data.append(yaml.dump(overlay).replace('null', ''))
289289
yaml_data = "---\n".join(_yaml_data)
290290

291-
self.plan = await self.bundle_facade.GetChanges(
291+
self.plan = await self.bundle_facade.GetChangesMapArgs(
292292
bundleurl=entity_id,
293293
yaml=yaml_data)
294294

@@ -488,10 +488,12 @@ def sorted(self):
488488
class ChangeInfo:
489489
_toPy = {}
490490

491-
def __init__(self, change_id, requires):
491+
def __init__(self, change_id, requires, params=None):
492492
self.change_id = change_id
493493
self.requires = requires
494494

495+
type(self).from_dict(self, params)
496+
495497
@classmethod
496498
def from_dict(cls, self, data):
497499
"""from_dict converts a data bag into fields on a class instance.
@@ -538,39 +540,6 @@ class AddApplicationChange(ChangeInfo):
538540
:local_resources: identifies the path to the local resource of the
539541
application's charm.
540542
"""
541-
542-
def __init__(self, change_id, requires, params=None):
543-
super(AddApplicationChange, self).__init__(change_id, requires)
544-
545-
if isinstance(params, list):
546-
self.charm = params[0]
547-
self.series = params[1]
548-
self.application = params[2]
549-
self.options = params[3]
550-
self.constraints = params[4]
551-
self.storage = {k: parse_storage_constraint(v) for k, v in params[5].items()}
552-
self.channel = None
553-
if len(params) == 8:
554-
# Juju 2.4 and below only sends the endpoint bindings and resources
555-
self.endpoint_bindings = params[6]
556-
self.resources = params[7]
557-
self.devices = None
558-
self.num_units = None
559-
else:
560-
# Juju 2.5+ sends devices before endpoint bindings, as well as num_units
561-
# There might be placement but we need to ignore that.
562-
self.devices = {k: parse_device_constraint(v) for k, v in params[6].items()}
563-
self.endpoint_bindings = params[7]
564-
self.resources = params[8]
565-
self.num_units = params[9]
566-
if len(params) > 10:
567-
self.channel = params[10]
568-
569-
elif isinstance(params, dict):
570-
AddApplicationChange.from_dict(self, params)
571-
else:
572-
raise Exception("unexpected params type")
573-
574543
@staticmethod
575544
def method():
576545
"""method returns an associated ID for the Juju API call.
@@ -685,26 +654,6 @@ class AddCharmChange(ChangeInfo):
685654
not sufficient.
686655
:channel: preferred channel for obtaining the charm.
687656
"""
688-
689-
def __init__(self, change_id, requires, params=None):
690-
super(AddCharmChange, self).__init__(change_id, requires)
691-
692-
if isinstance(params, list):
693-
self.charm = params[0]
694-
self.series = params[1]
695-
if len(params) > 2 and params[2] != "":
696-
self.channel = params[2]
697-
else:
698-
self.channel = None
699-
if len(params) > 3 and params[3] != "":
700-
self.architecture = params[3]
701-
else:
702-
self.architecture = None
703-
elif isinstance(params, dict):
704-
AddCharmChange.from_dict(self, params)
705-
else:
706-
raise Exception("unexpected params type")
707-
708657
@staticmethod
709658
def method():
710659
"""method returns an associated ID for the Juju API call.
@@ -787,21 +736,6 @@ class AddMachineChange(ChangeInfo):
787736
"lxc" or kvm"). It is not specified for top level machines.
788737
:parent_id: id of the parent machine.
789738
"""
790-
791-
def __init__(self, change_id, requires, params=None):
792-
super(AddMachineChange, self).__init__(change_id, requires)
793-
# this one is weird, as it returns a set of parameters inside a list.
794-
if isinstance(params, list):
795-
options = params[0] or {}
796-
self.series = options.get("series")
797-
self.constraints = options.get("constraints")
798-
self.container_type = options.get("containerType")
799-
self.parent_id = options.get("parentId")
800-
elif isinstance(params, dict):
801-
AddMachineChange.from_dict(self, params)
802-
else:
803-
raise Exception("unexpected params type")
804-
805739
@staticmethod
806740
def method():
807741
"""method returns an associated ID for the Juju API call.
@@ -879,18 +813,6 @@ class AddRelationChange(ChangeInfo):
879813
application, and the interface is optional. Examples are
880814
"$deploy-42:web", "$deploy-42", "mysql:db".
881815
"""
882-
883-
def __init__(self, change_id, requires, params=None):
884-
super(AddRelationChange, self).__init__(change_id, requires)
885-
886-
if isinstance(params, list):
887-
self.endpoint1 = params[0]
888-
self.endpoint2 = params[1]
889-
elif isinstance(params, dict):
890-
AddRelationChange.from_dict(self, params)
891-
else:
892-
raise Exception("unexpected params type")
893-
894816
@staticmethod
895817
def method():
896818
"""method returns an associated ID for the Juju API call.
@@ -941,18 +863,6 @@ class AddUnitChange(ChangeInfo):
941863
:to: optional location where to add the unit, as a placeholder
942864
pointing to another unit change or to a machine change.
943865
"""
944-
945-
def __init__(self, change_id, requires, params=None):
946-
super(AddUnitChange, self).__init__(change_id, requires)
947-
948-
if isinstance(params, list):
949-
self.application = params[0]
950-
self.to = params[1]
951-
elif isinstance(params, dict):
952-
AddUnitChange.from_dict(self, params)
953-
else:
954-
raise Exception("unexpected params type")
955-
956866
@staticmethod
957867
def method():
958868
"""method returns an associated ID for the Juju API call.
@@ -1010,19 +920,6 @@ class CreateOfferChange(ChangeInfo):
1010920
offer.
1011921
:offer_name: describes the offer name.
1012922
"""
1013-
1014-
def __init__(self, change_id, requires, params=None):
1015-
super(CreateOfferChange, self).__init__(change_id, requires)
1016-
1017-
if isinstance(params, list):
1018-
self.application = params[0]
1019-
self.endpoints = params[1]
1020-
self.offer_name = params[2]
1021-
elif isinstance(params, dict):
1022-
CreateOfferChange.from_dict(self, params)
1023-
else:
1024-
raise Exception("unexpected params type")
1025-
1026923
@staticmethod
1027924
def method():
1028925
"""method returns an associated ID for the Juju API call.
@@ -1065,18 +962,6 @@ class ConsumeOfferChange(ChangeInfo):
1065962
:url: contains the location of the offer
1066963
:application_name: describes the application name on offer.
1067964
"""
1068-
1069-
def __init__(self, change_id, requires, params=None):
1070-
super(ConsumeOfferChange, self).__init__(change_id, requires)
1071-
1072-
if isinstance(params, list):
1073-
self.url = params[0]
1074-
self.application_name = params[1]
1075-
elif isinstance(params, dict):
1076-
ConsumeOfferChange.from_dict(self, params)
1077-
else:
1078-
raise Exception("unexpected params type")
1079-
1080965
@staticmethod
1081966
def method():
1082967
"""method returns an associated ID for the Juju API call.
@@ -1121,18 +1006,6 @@ class ExposeChange(ChangeInfo):
11211006
that should be able to access the port ranges that the application
11221007
has opened for each endpoint.
11231008
"""
1124-
1125-
def __init__(self, change_id, requires, params=None):
1126-
super(ExposeChange, self).__init__(change_id, requires)
1127-
1128-
self.exposed_endpoints = None
1129-
if isinstance(params, list):
1130-
self.application = params[0]
1131-
elif isinstance(params, dict):
1132-
ExposeChange.from_dict(self, params)
1133-
else:
1134-
raise Exception("unexpected params type")
1135-
11361009
@staticmethod
11371010
def method():
11381011
"""method returns an associated ID for the Juju API call.
@@ -1171,18 +1044,6 @@ class ScaleChange(ChangeInfo):
11711044
:application: placeholder name of the application to be scaled.
11721045
:scale: is the new scale value to use.
11731046
"""
1174-
1175-
def __init__(self, change_id, requires, params=None):
1176-
super(ScaleChange, self).__init__(change_id, requires)
1177-
1178-
if isinstance(params, list):
1179-
self.application = params[0]
1180-
self.scale = params[1]
1181-
elif isinstance(params, dict):
1182-
ScaleChange.from_dict(self, params)
1183-
else:
1184-
raise Exception("unexpected params type")
1185-
11861047
@staticmethod
11871048
def method():
11881049
"""method returns an associated ID for the Juju API call.
@@ -1224,19 +1085,6 @@ class SetAnnotationsChange(ChangeInfo):
12241085
:entity_type: type of the entity, "application" or "machine".
12251086
:ennotations: annotations as key/value pairs.
12261087
"""
1227-
1228-
def __init__(self, change_id, requires, params=None):
1229-
super(SetAnnotationsChange, self).__init__(change_id, requires)
1230-
1231-
if isinstance(params, list):
1232-
self.id = params[0]
1233-
self.entity_type = params[1]
1234-
self.annotations = params[2]
1235-
elif isinstance(params, dict):
1236-
SetAnnotationsChange.from_dict(self, params)
1237-
else:
1238-
raise Exception("unexpected params type")
1239-
12401088
@staticmethod
12411089
def method():
12421090
"""method returns an associated ID for the Juju API call.

tests/integration/test_crossmodel.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ async def test_relate_with_offer(event_loop):
142142

143143
@base.bootstrapped
144144
@pytest.mark.asyncio
145+
@pytest.mark.bundle
145146
async def test_add_bundle(event_loop):
146147
pytest.skip("skip until we have a faster example to test")
147148
tests_dir = Path(__file__).absolute().parent

tests/integration/test_model.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ async def test_model_name(event_loop):
3636

3737
@base.bootstrapped
3838
@pytest.mark.asyncio
39+
@pytest.mark.bundle
3940
async def test_deploy_local_bundle_dir(event_loop):
4041
bundle_path = TESTS_DIR / 'bundle'
4142

@@ -56,6 +57,7 @@ async def test_deploy_local_bundle_dir(event_loop):
5657

5758
@base.bootstrapped
5859
@pytest.mark.asyncio
60+
@pytest.mark.bundle
5961
async def test_deploy_local_bundle_file(event_loop):
6062
bundle_path = TESTS_DIR / 'bundle'
6163
mini_bundle_file_path = bundle_path / 'mini-bundle.yaml'
@@ -73,6 +75,7 @@ async def test_deploy_local_bundle_file(event_loop):
7375

7476
@base.bootstrapped
7577
@pytest.mark.asyncio
78+
@pytest.mark.bundle
7679
async def test_deploy_bundle_local_resource_relative_path(event_loop):
7780
bundle_file_path = INTEGRATION_TEST_DIR / 'bundle-file-resource.yaml'
7881

@@ -126,6 +129,7 @@ async def test_deploy_by_revision_validate_flags(event_loop):
126129

127130
@base.bootstrapped
128131
@pytest.mark.asyncio
132+
@pytest.mark.bundle
129133
async def test_deploy_local_bundle_include_file(event_loop):
130134
bundle_dir = INTEGRATION_TEST_DIR / 'bundle'
131135
bundle_yaml_path = bundle_dir / 'bundle-include-file.yaml'
@@ -143,6 +147,7 @@ async def test_deploy_local_bundle_include_file(event_loop):
143147

144148
@base.bootstrapped
145149
@pytest.mark.asyncio
150+
@pytest.mark.bundle
146151
async def test_deploy_local_bundle_include_base64(event_loop):
147152
bundle_dir = INTEGRATION_TEST_DIR / 'bundle'
148153
bundle_yaml_path = bundle_dir / 'bundle-include-base64.yaml'
@@ -159,6 +164,7 @@ async def test_deploy_local_bundle_include_base64(event_loop):
159164

160165
@base.bootstrapped
161166
@pytest.mark.asyncio
167+
@pytest.mark.bundle
162168
async def test_deploy_bundle_local_charms(event_loop):
163169
bundle_path = INTEGRATION_TEST_DIR / 'bundle' / 'local.yaml'
164170

@@ -174,6 +180,7 @@ async def test_deploy_bundle_local_charms(event_loop):
174180

175181
@base.bootstrapped
176182
@pytest.mark.asyncio
183+
@pytest.mark.bundle
177184
async def test_deploy_invalid_bundle(event_loop):
178185
pytest.skip('test_deploy_invalid_bundle intermittent test failure')
179186
bundle_path = TESTS_DIR / 'bundle' / 'invalid.yaml'
@@ -252,6 +259,7 @@ async def test_wait_local_charm_waiting_timeout(event_loop):
252259

253260
@base.bootstrapped
254261
@pytest.mark.asyncio
262+
@pytest.mark.bundle
255263
async def test_deploy_bundle(event_loop):
256264
async with base.CleanModel() as model:
257265
await model.deploy('anbox-cloud-core', channel='stable',
@@ -263,6 +271,7 @@ async def test_deploy_bundle(event_loop):
263271

264272
@base.bootstrapped
265273
@pytest.mark.asyncio
274+
@pytest.mark.bundle
266275
async def test_deploy_local_bundle_with_overlay_multi(event_loop):
267276
async with base.CleanModel() as model:
268277
bundle_with_overlay_path = OVERLAYS_DIR / 'bundle-with-overlay-multi.yaml'
@@ -276,6 +285,7 @@ async def test_deploy_local_bundle_with_overlay_multi(event_loop):
276285

277286
@base.bootstrapped
278287
@pytest.mark.asyncio
288+
@pytest.mark.bundle
279289
async def test_deploy_bundle_with_overlay_as_argument(event_loop):
280290
async with base.CleanModel() as model:
281291
overlay_path = OVERLAYS_DIR / 'test-overlay.yaml'
@@ -297,6 +307,7 @@ async def test_deploy_bundle_with_overlay_as_argument(event_loop):
297307

298308
@base.bootstrapped
299309
@pytest.mark.asyncio
310+
@pytest.mark.bundle
300311
async def test_deploy_bundle_with_multi_overlay_as_argument(event_loop):
301312
async with base.CleanModel() as model:
302313
overlay_path = OVERLAYS_DIR / 'test-multi-overlay.yaml'
@@ -309,6 +320,7 @@ async def test_deploy_bundle_with_multi_overlay_as_argument(event_loop):
309320

310321
@base.bootstrapped
311322
@pytest.mark.asyncio
323+
@pytest.mark.bundle
312324
async def test_deploy_bundle_with_multiple_overlays_with_include_files(event_loop):
313325
async with base.CleanModel() as model:
314326
bundle_yaml_path = TESTS_DIR / 'integration' / 'bundle' / 'bundle.yaml'
@@ -357,6 +369,7 @@ async def test_deploy_from_ch_channel_revision_success(event_loop):
357369

358370
@base.bootstrapped
359371
@pytest.mark.asyncio
372+
@pytest.mark.bundle
360373
async def test_deploy_trusted_bundle(event_loop):
361374
pytest.skip("skip until we have a deployable bundle available. Right now the landscape-dense fails because postgresql is broken")
362375
async with base.CleanModel() as model:
@@ -767,6 +780,7 @@ async def test_attach_resource(event_loop):
767780

768781
@base.bootstrapped
769782
@pytest.mark.asyncio
783+
@pytest.mark.bundle
770784
async def test_store_resources_bundle(event_loop):
771785
pytest.skip('test_store_resources_bundle intermittent test failure')
772786
async with base.CleanModel() as model:
@@ -789,6 +803,7 @@ async def test_store_resources_bundle(event_loop):
789803

790804
@base.bootstrapped
791805
@pytest.mark.asyncio
806+
@pytest.mark.bundle
792807
async def test_store_resources_bundle_revs(event_loop):
793808
pytest.skip('test_store_resources_bundle_revs intermittent test failure')
794809
async with base.CleanModel() as model:

0 commit comments

Comments
 (0)