Skip to content

Commit ae26301

Browse files
authored
Allow Harness.get_relation_data to accept instances (canonical#465)
* Allow Harness.get_relation_data to accept instances It's tripped me up a few times that you have to pass in the name of the app or unit rather than the instance itself that you use everywhere else, so this allows get_relation_data to handle either. * Update to fix test suite with latest changes to operator framework
1 parent 28c96ba commit ae26301

File tree

6 files changed

+36
-13
lines changed

6 files changed

+36
-13
lines changed

ops/charm.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,7 @@ class ContainerMeta:
895895
name: Name of container (key in the YAML)
896896
mounts: :class:`ContainerStorageMeta` mounts available to the container
897897
"""
898+
898899
def __init__(self, name, raw):
899900
self.name = name
900901
self._mounts = {}
@@ -956,6 +957,7 @@ class ContainerStorageMeta:
956957
`location` will not be an accessible attribute, as it would not be possible to determine
957958
which mount point was desired, and `locations` should be iterated over.
958959
"""
960+
959961
def __init__(self, storage, location):
960962
self.storage = storage
961963
self._locations = [location]

ops/model.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ def __init__(self, name, meta, backend, cache):
296296
self._is_our_unit = self.name == self._backend.unit_name
297297
self._status = None
298298

299-
if self._is_our_unit:
299+
if self._is_our_unit and hasattr(meta, "containers"):
300300
self._containers = ContainerMapping(meta.containers, backend)
301301

302302
def _invalidate(self):
@@ -1052,6 +1052,7 @@ class Container:
10521052
Attributes:
10531053
name: The name of the container from metadata.yaml (eg, 'postgres').
10541054
"""
1055+
10551056
def __init__(self, name, backend, pebble_client=None):
10561057
self.name = name
10571058

ops/testing.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@
3737
# pass in a file-like object or the string directly.
3838
OptionalYAML = typing.Optional[typing.Union[str, typing.TextIO]]
3939

40+
41+
# An instance of an Application or Unit, or the name of either.
42+
# This is done here to avoid a scoping issue with the `model` property
43+
# of the Harness class below.
44+
AppUnitOrName = typing.Union[str, model.Application, model.Unit]
45+
46+
4047
# CharmType represents user charms that are derived from CharmBase.
4148
CharmType = typing.TypeVar('CharmType', bound=charm.CharmBase)
4249

@@ -72,6 +79,7 @@ class Harness(typing.Generic[CharmType]):
7279
config.yaml. If not supplied, we will look for a 'config.yaml' file in the
7380
parent directory of the Charm.
7481
"""
82+
7583
def __init__(
7684
self,
7785
charm_cls: typing.Type[CharmType],
@@ -578,7 +586,7 @@ def _emit_relation_departed(self, relation_id, unit_name):
578586
raise ValueError('Invalid Unit Name')
579587
self._charm.on[rel_name].relation_departed.emit(relation, app, unit)
580588

581-
def get_relation_data(self, relation_id: int, app_or_unit: str) -> typing.Mapping:
589+
def get_relation_data(self, relation_id: int, app_or_unit: AppUnitOrName) -> typing.Mapping:
582590
"""Get the relation data bucket for a single app or unit in a given relation.
583591
584592
This ignores all of the safety checks of who can and can't see data in relations (eg,
@@ -587,13 +595,15 @@ def get_relation_data(self, relation_id: int, app_or_unit: str) -> typing.Mappin
587595
588596
Args:
589597
relation_id: The relation whose content we want to look at.
590-
app_or_unit: The name of the application or unit whose data we want to read
598+
app_or_unit: An Application or Unit instance, or its name, whose data we want to read
591599
Return:
592600
A dict containing the relation data for `app_or_unit` or None.
593601
594602
Raises:
595603
KeyError: if relation_id doesn't exist
596604
"""
605+
if hasattr(app_or_unit, 'name'):
606+
app_or_unit = app_or_unit.name
597607
return self._backend._relation_data[relation_id].get(app_or_unit, None)
598608

599609
def get_pod_spec(self) -> (typing.Mapping, typing.Mapping):

test/test_charm.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -473,8 +473,8 @@ def test_containers_storage_multiple_mounts(self):
473473
self.assertIsInstance(meta.containers['test1'], ContainerMeta)
474474
self.assertIsInstance(meta.containers['test1'].mounts["data"], ContainerStorageMeta)
475475
self.assertEqual(
476-
meta.containers['test1'].mounts["data"].locations[0],
477-
'/test/storagemount')
476+
meta.containers['test1'].mounts["data"].locations[0],
477+
'/test/storagemount')
478478
self.assertEqual(meta.containers['test1'].mounts["data"].locations[1], '/test/otherdata')
479479

480480
with self.assertRaises(RuntimeError):

test/test_pebble.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -469,13 +469,13 @@ def test_service_equality(self):
469469
raw={
470470
"override": "replace",
471471
"command": "echo foo"
472-
})
472+
})
473473
old_services = {"foo": old_service}
474474
self.assertEqual(plan.services, old_services)
475475

476476
services_as_dict = {
477477
"foo": {"override": "replace", "command": "echo foo"}
478-
}
478+
}
479479
self.assertEqual(plan.services, services_as_dict)
480480

481481

test/test_testing.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,16 @@
3232
Object,
3333
)
3434
from ops.model import (
35+
Application,
3536
ActiveStatus,
3637
MaintenanceStatus,
3738
UnknownStatus,
3839
ModelError,
3940
RelationNotFoundError,
4041
_ModelBackend,
42+
Unit,
4143
)
42-
from ops.testing import (
43-
Harness,
44-
_TestingPebbleClient,
45-
)
44+
from ops.testing import Harness, _TestingPebbleClient
4645

4746

4847
class TestHarness(unittest.TestCase):
@@ -548,12 +547,13 @@ def test_relation_events(self):
548547
}}])
549548

550549
def test_get_relation_data(self):
551-
harness = Harness(CharmBase, meta='''
550+
charm_meta = '''
552551
name: test-app
553552
requires:
554553
db:
555554
interface: pgsql
556-
''')
555+
'''
556+
harness = Harness(CharmBase, meta=charm_meta)
557557
self.addCleanup(harness.cleanup)
558558
rel_id = harness.add_relation('db', 'postgresql')
559559
harness.update_relation_data(rel_id, 'postgresql', {'remote': 'data'})
@@ -565,6 +565,16 @@ def test_get_relation_data(self):
565565
# unknown relation id
566566
harness.get_relation_data(99, 'postgresql')
567567

568+
meta = yaml.safe_load(charm_meta)
569+
t_app = Application('test-app', meta, harness._backend, None)
570+
t_unit0 = Unit('test-app/0', meta, harness._backend, {Application: t_app})
571+
t_unit1 = Unit('test-app/1', meta, harness._backend, {Application: t_app})
572+
self.assertEqual(harness.get_relation_data(rel_id, t_app), {})
573+
self.assertEqual(harness.get_relation_data(rel_id, t_unit0), {})
574+
self.assertEqual(harness.get_relation_data(rel_id, t_unit1), None)
575+
pg_app = Application('postgresql', meta, harness._backend, None)
576+
self.assertEqual(harness.get_relation_data(rel_id, pg_app), {'remote': 'data'})
577+
568578
def test_create_harness_twice(self):
569579
metadata = '''
570580
name: my-charm

0 commit comments

Comments
 (0)