diff --git a/watcher/api/controllers/v1/audit.py b/watcher/api/controllers/v1/audit.py index 7a247dc11..8fe2a22e7 100644 --- a/watcher/api/controllers/v1/audit.py +++ b/watcher/api/controllers/v1/audit.py @@ -636,4 +636,11 @@ class AuditsController(rest.RestController): policy.enforce(context, 'audit:update', audit_to_delete, action='audit:update') + initial_state = audit_to_delete.state + new_state = objects.audit.State.DELETED + if not objects.audit.AuditStateTransitionManager( + ).check_transition(initial_state, new_state): + raise exception.DeleteError( + state=initial_state) + audit_to_delete.soft_delete() diff --git a/watcher/common/exception.py b/watcher/common/exception.py index f4d7404c4..afae2f307 100644 --- a/watcher/common/exception.py +++ b/watcher/common/exception.py @@ -332,6 +332,10 @@ class PatchError(Invalid): msg_fmt = _("Couldn't apply patch '%(patch)s'. Reason: %(reason)s") +class DeleteError(Invalid): + msg_fmt = _("Couldn't delete when state is '%(state)s'.") + + # decision engine class WorkflowExecutionException(WatcherException): diff --git a/watcher/tests/api/v1/test_actions_plans.py b/watcher/tests/api/v1/test_actions_plans.py index 0f5d5d4ef..950ece523 100644 --- a/watcher/tests/api/v1/test_actions_plans.py +++ b/watcher/tests/api/v1/test_actions_plans.py @@ -147,6 +147,11 @@ class TestListActionPlan(api_base.FunctionalTest): audit_id=audit2.id) action_plan_list.append(action_plan.uuid) + new_state = objects.audit.State.CANCELLED + self.patch_json( + '/audits/%s' % audit1.uuid, + [{'path': '/state', 'value': new_state, + 'op': 'replace'}]) self.delete('/audits/%s' % audit1.uuid) response = self.get_json('/action_plans') diff --git a/watcher/tests/api/v1/test_audits.py b/watcher/tests/api/v1/test_audits.py index d3ea0ffd3..1b425b02b 100644 --- a/watcher/tests/api/v1/test_audits.py +++ b/watcher/tests/api/v1/test_audits.py @@ -828,6 +828,23 @@ class TestDelete(api_base.FunctionalTest): def test_delete_audit(self, mock_utcnow): test_time = datetime.datetime(2000, 1, 1, 0, 0) mock_utcnow.return_value = test_time + + new_state = objects.audit.State.ONGOING + self.patch_json( + '/audits/%s' % self.audit.uuid, + [{'path': '/state', 'value': new_state, + 'op': 'replace'}]) + response = self.delete('/audits/%s' % self.audit.uuid, + expect_errors=True) + self.assertEqual(400, response.status_int) + self.assertEqual('application/json', response.content_type) + self.assertTrue(response.json['error_message']) + + new_state = objects.audit.State.CANCELLED + self.patch_json( + '/audits/%s' % self.audit.uuid, + [{'path': '/state', 'value': new_state, + 'op': 'replace'}]) self.delete('/audits/%s' % self.audit.uuid) response = self.get_json('/audits/%s' % self.audit.uuid, expect_errors=True)