As I worked on jhack show-relation
it became apparent how having a clearly structured way of addressing relation data is fundamental to being able to understand the ‘relation’ data structure itself.
Show-relation reasons in terms of:
- provider:
- app data: Dict[str, str]
- unit data: Dict[int, Dict[str, str]
- metadata:
- scale: int
- units: Tuple[int] # the IDs of the units of this application
- leader_id: int # which one of the units is leader
- interface: str # name of the relation interface used by this provider
- requirer: same tree as provider.
Then I thought, wouldn’t it be nice if the testing code we wrote, regardless of it’s unit tests using the Harness, or integration tests using python-libjuju (or any other framework, for that matters), also used the same structure?
I would like my tests to be like:
def test_relation_data():
data = get_data(provider_endpoint='my_app:db', requirer_endpoint='remote:db')
assert data.provider.app_data['foo'] == 'bar'
for unit in data.requirer.meta.units:
assert data.requirer.units_data[unit]['baz'] == 'qux'
Fortunately, the engine for producing this structure was already written! I extracted the source code from jhack show-relation
and packaged it in a standalone charm lib under the harness-extensions
collection, as it’s about facilitating testing.
How to use
charmcraft fetch-lib charms.harness_extensions.v0.relation_data_wrapper
# for integration tests, pytest-operator, or zaza context:
from charms.harness_extensions.v0.relation_data_wrapper import get_relation_data_from_juju
# for unit tests:
from charms.harness_extensions.v0.relation_data_wrapper import get_relation_data_from_harness
def test_unit(h:Harness):
r_id = h.add_relation('ingress', 'remote')
h.add_relation_unit(r_id, 'remote/0')
h.add_relation_unit(r_id, 'remote/1')
h.update_relation_data(r_id, 'local', {'lapp': 'data'})
h.update_relation_data(r_id, 'local/0', {'lunit0': 'data0'})
h.update_relation_data(r_id, 'remote', {'rapp': 'data'})
h.update_relation_data(r_id, 'remote/0', {'unit0': 'data0'})
h.update_relation_data(r_id, 'remote/1', {'unit1': 'data1'})
rdata = get_relation_data_from_harness(
h,
provider_endpoint='local:ingress',
requirer_endpoint='remote:ingress'
)
def test_integration(ops_test):
# deploy and relate traefik ('trfk') and prometheus ('prom') ...
rdata = get_relation_data_from_juju(requirer_endpoint='prom:ingress',
provider_endpoint='trfk:ingress-per-unit')
assert rdata.provider.app_name == 'trfk'
assert set(rdata.provider.units_data) == {0}
At the moment this is a proof of concept, but if it takes off, why not, we could include this in the Harness, python-libjuju, zaza, or whatever testing framework you use!