Skip to content

Commit 9d6dea6

Browse files
authored
Forward port subordinate utils (#880)
* Add subordinate utilities for unit.Unit
1 parent d75b1f9 commit 9d6dea6

4 files changed

Lines changed: 70 additions & 0 deletions

File tree

juju/application.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ def units(self):
6464
if unit.application == self.name
6565
]
6666

67+
@property
68+
def subordinate_units(self):
69+
"""Returns the subordinate units of this application"""
70+
return [u for u in self.units if u.is_subordinate]
71+
6772
@property
6873
def relations(self):
6974
return [rel for rel in self.model.relations if rel.matches(self.name)]

juju/model.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@ def units(self):
169169
"""
170170
return self._live_entity_map('unit')
171171

172+
@property
173+
def subordinate_units(self):
174+
"""Return a map of unit-id:Unit for all subordinate units"""
175+
return {u_name: u for u_name, u in self.units.items() if u.is_subordinate}
176+
172177
@property
173178
def relations(self):
174179
"""Return a map of relation-id:Relation for all relations currently in
@@ -1078,6 +1083,14 @@ def units(self):
10781083
"""
10791084
return self.state.units
10801085

1086+
@property
1087+
def subordinate_units(self):
1088+
"""Return a map of unit-id:Unit for all subordiante units currently in
1089+
the model.
1090+
1091+
"""
1092+
return self.state.subordinate_units
1093+
10811094
@property
10821095
def relations(self):
10831096
"""Return a list of all Relations currently in the model.

juju/unit.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@ def agent_status_since(self):
2626
"""
2727
return pyrfc3339.parse(self.safe_data['agent-status']['since'])
2828

29+
@property
30+
def is_subordinate(self):
31+
"""True if the unit is subordinate of another unit
32+
33+
"""
34+
return self.safe_data['subordinate']
35+
36+
@property
37+
def principal_unit(self):
38+
"""Returns the name of the unit of which this unit is a subordinate to.
39+
Returns '' for principal units themselves.
40+
"""
41+
return self.safe_data['principal']
42+
2943
@property
3044
def agent_status_message(self):
3145
"""Get the agent status message.
@@ -77,6 +91,14 @@ def public_address(self):
7791
def tag(self):
7892
return tag.unit(self.name)
7993

94+
def get_subordinates(self):
95+
"""Returns the unit objects that are subordinates to this unit
96+
97+
:return [Unit]
98+
"""
99+
return [u for u_name, u in self.model.units.items() if u.is_subordinate and
100+
u.principal_unit == self.name]
101+
80102
async def destroy(self):
81103
"""Destroy this unit.
82104

tests/integration/test_unit.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,3 +255,33 @@ async def test_unit_introspect(event_loop):
255255
series='jammy',
256256
to='0',
257257
)
258+
259+
260+
@base.bootstrapped
261+
@pytest.mark.asyncio
262+
async def test_subordinate_units(event_loop):
263+
async with base.CleanModel() as model:
264+
u_app = await model.deploy('ubuntu')
265+
n_app = await model.deploy('ntp')
266+
await model.relate('ubuntu', 'ntp')
267+
await model.wait_for_idle()
268+
269+
# model subordinates
270+
model_subs = model.subordinate_units
271+
assert len(model_subs) == 1
272+
assert 'ntp/0' in model_subs
273+
assert 'ubuntu/0' not in model_subs
274+
275+
n_unit = model_subs['ntp/0']
276+
u_unit = u_app.units[0]
277+
278+
# application subordinates
279+
app_sub_names = [u.name for u in n_app.subordinate_units]
280+
assert n_unit.name in app_sub_names
281+
assert u_unit.name not in app_sub_names
282+
283+
assert n_unit.is_subordinate
284+
assert not u_unit.is_subordinate
285+
assert n_unit.principal_unit == 'ubuntu/0'
286+
assert u_unit.principal_unit == ''
287+
assert [u.name for u in u_unit.get_subordinates()] == [n_unit.name]

0 commit comments

Comments
 (0)