Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove "dying" units from planned units #936

Merged
Merged
10 changes: 7 additions & 3 deletions ops/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -2931,16 +2931,20 @@ def get_pebble(self, socket_path: str) -> 'Client':
def planned_units(self) -> int:
"""Count of "planned" units that will run this application.

Includes the current unit in the count.
Includes the current unit in the count, but does not include dying units.
Mehdi-Bendriss marked this conversation as resolved.
Show resolved Hide resolved

"""
# The goal-state tool will return the information that we need. Goal state as a general
# concept is being deprecated, however, in favor of approaches such as the one that we use
# here.
app_state = self._run('goal-state', return_output=True, use_json=True)
app_state = typing.cast(Dict[str, List[str]], app_state)
app_state = typing.cast(Dict[str, Dict[str, Any]], app_state)
benhoyt marked this conversation as resolved.
Show resolved Hide resolved

# Planned units can be zero. We don't need to do error checking here.
return len(app_state.get('units', []))
# But we need to filter out dying units as they may be reported before being deleted
units = app_state.get('units', {})
num_alive = sum(1 for unit in units.values() if unit['status'] != 'dying')
return num_alive

def update_relation_data(self, relation_id: int, _entity: 'UnitOrApplication',
key: str, value: str):
Expand Down
29 changes: 29 additions & 0 deletions test/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -2712,6 +2712,35 @@ def test_relation_remote_app_name_script_errors(self):
['relation-list', '-r', '6', '--app', '--format=json'],
])

def test_planned_units(self):
# no units
fake_script(self, 'goal-state', """
echo '{"units":{}, "relations":{}}'
""")
self.assertEqual(self.backend.planned_units(), 0)

# only active units
fake_script(self, 'goal-state', """
echo '{
"units":{
"app/0": {"status":"active","since":"2023-05-23 17:05:05Z"},
"app/1": {"status":"active","since":"2023-05-23 17:57:05Z"}
},
"relations": {}
}'""")
self.assertEqual(self.backend.planned_units(), 2)

# active and dying units
fake_script(self, 'goal-state', """
echo '{
"units":{
"app/0": {"status":"active","since":"2023-05-23 17:05:05Z"},
"app/1": {"status":"dying","since":"2023-05-23 17:57:05Z"}
},
"relations": {}
}'""")
self.assertEqual(self.backend.planned_units(), 1)


class TestLazyMapping(unittest.TestCase):

Expand Down