diff --git a/ops/model.py b/ops/model.py index be11dffb8..f04b95b4e 100644 --- a/ops/model.py +++ b/ops/model.py @@ -1791,6 +1791,8 @@ class UnknownStatus(StatusBase): A unit-agent has finished calling install, config-changed and start, but the charm has not called status-set yet. + This status is read-only; trying to set unit or application status to + ``UnknownStatus`` will raise :class:`ModelError`. """ name = 'unknown' @@ -1808,6 +1810,9 @@ class ErrorStatus(StatusBase): The unit-agent has encountered an error (the application or unit requires human intervention in order to operate correctly). + + This status is read-only; trying to set unit or application status to + ``ErrorStatus`` will raise :class:`ModelError`. """ name = 'error' diff --git a/ops/testing.py b/ops/testing.py index 19d44fab6..e399747f3 100644 --- a/ops/testing.py +++ b/ops/testing.py @@ -448,7 +448,7 @@ def begin_with_initial_hooks(self) -> None: # the unit status from "Maintenance" to "Unknown". See gh#726 post_setup_sts = self._backend.status_get() if post_setup_sts.get("status") == "maintenance" and not post_setup_sts.get("message"): - self._backend.status_set("unknown", "", is_app=False) + self._backend._unit_status = {'status': 'unknown', 'message': ''} all_ids = list(self._backend._relation_names.items()) random.shuffle(all_ids) for rel_id, rel_name in all_ids: @@ -2256,6 +2256,9 @@ def status_get(self, *, is_app: bool = False): return self._unit_status def status_set(self, status: '_StatusName', message: str = '', *, is_app: bool = False): + if status in [model.ErrorStatus.name, model.UnknownStatus.name]: + raise model.ModelError(f'ERROR invalid status "{status}", expected one of' + ' [maintenance blocked waiting active]') if is_app: self._app_status = {'status': status, 'message': message} else: diff --git a/test/test_testing.py b/test/test_testing.py index 1921daf04..f515f618b 100644 --- a/test/test_testing.py +++ b/test/test_testing.py @@ -2851,6 +2851,23 @@ def _on_collect_unit_status(self, event: ops.CollectStatusEvent): self.assertEqual(harness.model.app.status, ops.ActiveStatus('active app')) self.assertEqual(harness.model.unit.status, ops.ActiveStatus('active unit')) + def test_invalid_status_set(self): + harness = ops.testing.Harness(ops.CharmBase) + harness.set_leader(True) + harness.begin() + + with self.assertRaises(ops.model.ModelError): + harness.model.app.status = ops.UnknownStatus() + with self.assertRaises(ops.model.ModelError): + harness.model.app.status = ops.ErrorStatus() + harness.model.app.status = ops.ActiveStatus() + + with self.assertRaises(ops.model.ModelError): + harness.model.unit.status = ops.UnknownStatus() + with self.assertRaises(ops.model.ModelError): + harness.model.unit.status = ops.ErrorStatus() + harness.model.unit.status = ops.ActiveStatus() + class TestNetwork(unittest.TestCase): def setUp(self):