From 5bbc50faa49114cc6c4e0ae2b4b93351c33b6bbd Mon Sep 17 00:00:00 2001 From: Martin Kalcok <69471063+mkalcok@users.noreply.github.com> Date: Tue, 25 Apr 2023 12:18:58 +0200 Subject: [PATCH] Backport ErrorStatus to 1.5 branch (#875) (#927) Add ErrorStatus so that getting Unit/App.status when the status is "error" does not raise. Co-authored-by: PietroPasotti Co-authored-by: Ben Hoyt --- ops/model.py | 10 ++++++++++ test/test_model.py | 47 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/ops/model.py b/ops/model.py index 9412ae989..6ade16cc7 100644 --- a/ops/model.py +++ b/ops/model.py @@ -1158,6 +1158,16 @@ def __repr__(self): return "UnknownStatus()" +@StatusBase.register +class ErrorStatus(StatusBase): + """The unit status is error. + + The unit-agent has encountered an error (the application or unit requires + human intervention in order to operate correctly). + """ + name = 'error' + + @StatusBase.register class ActiveStatus(StatusBase): """The unit is ready. diff --git a/test/test_model.py b/test/test_model.py index 173168fa6..9e45b28ff 100755 --- a/test/test_model.py +++ b/test/test_model.py @@ -2283,7 +2283,7 @@ def test_status_is_app_forced_kwargs(self): case() def test_local_set_invalid_status(self): - # juju return exit code 1 if you ask to set status to 'unknown' + # juju returns exit code 1 if you ask to set status to 'unknown' or 'error' meta = ops.charm.CharmMeta.from_yaml(''' name: myapp ''') @@ -2293,20 +2293,65 @@ def test_local_set_invalid_status(self): with self.assertRaises(ops.model.ModelError): model.unit.status = ops.model.UnknownStatus() + with self.assertRaises(ops.model.ModelError): + model.unit.status = ops.model.ErrorStatus() self.assertEqual(fake_script_calls(self, True), [ ['status-set', '--application=False', 'unknown', ''], + ['status-set', '--application=False', 'error', ''], ]) with self.assertRaises(ops.model.ModelError): model.app.status = ops.model.UnknownStatus() + with self.assertRaises(ops.model.ModelError): + model.app.status = ops.model.ErrorStatus() # A leadership check is needed for application status. self.assertEqual(fake_script_calls(self, True), [ ['is-leader', '--format=json'], ['status-set', '--application=True', 'unknown', ''], + ['status-set', '--application=True', 'error', ''], ]) + def test_local_get_status(self): + for name, expected_cls in ( + ("active", ops.model.ActiveStatus), + ("waiting", ops.model.WaitingStatus), + ("blocked", ops.model.BlockedStatus), + ("maintenance", ops.model.MaintenanceStatus), + ("error", ops.model.ErrorStatus), + ): + meta = ops.charm.CharmMeta.from_yaml(''' + name: myapp + ''') + model = ops.model.Model(meta, self.backend) + + with self.subTest(name): + content = json.dumps({ + "message": "foo", + "status": name, + "status-data": {}, + }) + fake_script(self, 'status-get', "echo '{}'".format(content)) + + self.assertIsInstance(model.unit.status, expected_cls) + self.assertEqual(model.unit.status.name, name) + self.assertEqual(model.unit.status.message, "foo") + + content = json.dumps({ + "application-status": { + "message": "bar", + "status": name, + "status-data": {}, + } + }) + fake_script(self, 'status-get', "echo '{}'".format(content)) + fake_script(self, 'is-leader', 'echo true') + + self.assertIsInstance(model.app.status, expected_cls) + self.assertEqual(model.app.status.name, name) + self.assertEqual(model.app.status.message, "bar") + def test_status_set_is_app_not_bool_raises(self): for is_app_v in [None, 1, 2.0, 'a', b'beef', object]: with self.assertRaises(TypeError):