Added soft-delete orders/secrets support; Updated unit tests;

This commit is contained in:
John Wood
2013-05-16 23:52:33 -05:00
parent cc3f01bb95
commit 5edf183e78
4 changed files with 66 additions and 37 deletions

View File

@@ -30,6 +30,7 @@ from barbican.model.models import (Tenant, Secret, TenantSecret,
from barbican.model.repositories import (TenantRepo, SecretRepo,
OrderRepo, TenantSecretRepo,
EncryptedDatumRepo)
from barbican.common import exception
from barbican.crypto import extension_manager as em
from barbican.openstack.common.gettextutils import _
from barbican.openstack.common import jsonutils as json
@@ -47,12 +48,12 @@ def _general_failure(message):
def _secret_not_found():
"""Throw exception indicating secret not found."""
abort(falcon.HTTP_400, _('Unable to locate secret.'))
abort(falcon.HTTP_404, _('Unable to locate secret.'))
def _order_not_found():
"""Throw exception indicating order not found."""
abort(falcon.HTTP_400, _('Unable to locate order.'))
abort(falcon.HTTP_404, _('Unable to locate order.'))
def _put_accept_incorrect(ct):
@@ -363,9 +364,12 @@ class SecretResource(ApiResource):
_failed_to_create_encrypted_datum()
def on_delete(self, req, resp, tenant_id, secret_id):
secret = self.repo.get(entity_id=secret_id)
self.repo.delete_entity(secret)
try:
self.repo.delete_entity_by_id(entity_id=secret_id)
except exception.NotFound:
LOG.exception('Problem deleting secret')
_secret_not_found()
resp.status = falcon.HTTP_200
@@ -472,8 +476,11 @@ class OrderResource(ApiResource):
default=json_handler)
def on_delete(self, req, resp, tenant_id, order_id):
order = self.repo.get(entity_id=order_id)
self.repo.delete_entity(order)
try:
self.repo.delete_entity_by_id(entity_id=order_id)
except exception.NotFound:
LOG.exception('Problem deleting order')
_order_not_found()
resp.status = falcon.HTTP_200

View File

@@ -74,12 +74,17 @@ class ModelBase(object):
"""Delete this object"""
import barbican.model.repositories
session = session or barbican.model.repositories.get_session()
session.delete(self)
self.deleted = True
self.deleted_at = timeutils.utcnow()
self.save(session=session)
#TODO: Soft delete instead?
# self.deleted = True
# self.deleted_at = timeutils.utcnow()
# self.save(session=session)
self._do_delete_children(session)
def _do_delete_children(self, session):
"""
Sub-class hook: delete children relationships.
"""
pass
def update(self, values):
"""dict.update() behaviour."""
@@ -206,6 +211,13 @@ class Secret(BASE, ModelBase):
self.status = States.ACTIVE
def _do_delete_children(self, session):
"""
Sub-class hook: delete children relationships.
"""
for datum in self.encrypted_data:
datam.delete(session)
def _do_extra_dict_fields(self):
"""Sub-class hook method: return dict of fields."""
return {'secret_id': self.id,

View File

@@ -330,13 +330,20 @@ class BaseRepo(object):
"""
return self._update(entity_id, values, purge_props)
def delete_entity(self, entity):
"""Remove the entity"""
def delete_entity_by_id(self, entity_id):
"""Remove the entity by its ID"""
session = get_session()
with session.begin():
entity.deleted_at = timeutils.utcnow()
entity.delete(session=session)
entity = self.get(entity_id=entity_id, session=session)
try:
entity.delete(session=session)
except sqlalchemy.exc.IntegrityError:
LOG.exception('Problem finding entity to delete')
raise exception.NotFound("Entity ID %s not found"
% entity_id)
def _do_entity_name(self):
"""Sub-class hook: return entity name, such as for debugging."""
@@ -455,10 +462,6 @@ class TenantRepo(BaseRepo):
"""Sub-class hook: build a retrieve query."""
return session.query(models.Tenant).filter_by(id=entity_id)
def _do_validate(self, values):
"""Sub-class hook: validate values."""
pass
def find_by_keystone_id(self, keystone_id, suppress_exception=False,
session=None):
session = self.get_session(session)

View File

@@ -26,7 +26,7 @@ from barbican.crypto.extension_manager import CryptoExtensionManager
from barbican.model.models import (Secret, Tenant, TenantSecret,
Order, EncryptedDatum)
from barbican.common import config
from barbican.common import exception
from barbican.common import exception as excep
from barbican.openstack.common import jsonutils
@@ -325,7 +325,7 @@ class WhenGettingSecretsListUsingSecretsResource(unittest.TestCase):
self.resource.on_get(self.req, self.resp, self.tenant_id)
exception = cm.exception
self.assertEqual(falcon.HTTP_400, exception.status)
self.assertEqual(falcon.HTTP_404, exception.status)
def _create_url(self, tenant_id, offset_arg=None, limit_arg=None):
if limit_arg:
@@ -374,7 +374,7 @@ class WhenGettingPuttingOrDeletingSecretUsingSecretResource(
self.secret_repo = MagicMock()
self.secret_repo.get.return_value = self.secret
self.secret_repo.delete_entity.return_value = None
self.secret_repo.delete_entity_by_id.return_value = None
self.tenant_secret_repo = MagicMock()
self.tenant_secret_repo.create_from.return_value = None
@@ -434,7 +434,7 @@ class WhenGettingPuttingOrDeletingSecretUsingSecretResource(
self.secret.id)
exception = cm.exception
self.assertEqual(falcon.HTTP_400, exception.status)
self.assertEqual(falcon.HTTP_404, exception.status)
def test_should_throw_exception_for_get_when_accept_not_supported(self):
@@ -485,7 +485,7 @@ class WhenGettingPuttingOrDeletingSecretUsingSecretResource(
self.secret.id)
exception = cm.exception
self.assertEqual(falcon.HTTP_400, exception.status)
self.assertEqual(falcon.HTTP_404, exception.status)
def test_should_fail_put_secret_no_plain_text(self):
self._setup_for_puts()
@@ -517,17 +517,19 @@ class WhenGettingPuttingOrDeletingSecretUsingSecretResource(
self.resource.on_delete(self.req, self.resp, self.tenant_id,
self.secret.id)
self.secret_repo.get.assert_called_once_with(entity_id=self.secret.id)
self.secret_repo.delete_entity.assert_called_once_with(self.secret)
self.secret_repo.delete_entity_by_id.assert_called_once_with(entity_id=self.secret.id)
def test_should_throw_exception_for_delete_when_secret_not_found(self):
self.secret_repo.get.side_effect = exception.NotFound(
self.secret_repo.delete_entity_by_id.side_effect = excep.NotFound(
"Test not found exception")
with self.assertRaises(exception.NotFound):
with self.assertRaises(falcon.HTTPError) as cm:
self.resource.on_delete(self.req, self.resp, self.tenant_id,
self.secret.id)
exception = cm.exception
self.assertEqual(falcon.HTTP_404, exception.status)
def _setup_for_puts(self):
self.plain_text = "plain_text"
self.req.accept = self.mime_type
@@ -676,7 +678,7 @@ class WhenGettingOrdersListUsingOrdersResource(unittest.TestCase):
self.resource.on_get(self.req, self.resp, self.tenant_id)
exception = cm.exception
self.assertEqual(falcon.HTTP_400, exception.status)
self.assertEqual(falcon.HTTP_404, exception.status)
def _create_url(self, tenant_id, offset_arg=None, limit_arg=None):
if limit_arg:
@@ -700,7 +702,7 @@ class WhenGettingOrDeletingOrderUsingOrderResource(unittest.TestCase):
self.order_repo = MagicMock()
self.order_repo.get.return_value = self.order
self.order_repo.delete_entity.return_value = None
self.order_repo.delete_entity_by_id.return_value = None
self.policy = MagicMock()
@@ -722,26 +724,31 @@ class WhenGettingOrDeletingOrderUsingOrderResource(unittest.TestCase):
self.resource.on_delete(self.req, self.resp, self.tenant_keystone_id,
self.order.id)
self.order_repo.get.assert_called_once_with(entity_id=self.order.id)
self.order_repo.delete_entity.assert_called_once_with(self.order)
self.order_repo.delete_entity_by_id \
.assert_called_once_with(entity_id=self.order.id)
def test_should_throw_exception_for_get_when_order_not_found(self):
self.order_repo.get.side_effect = exception.NotFound(
"Test not found exception")
self.order_repo.get.return_value = None
with self.assertRaises(exception.NotFound):
with self.assertRaises(falcon.HTTPError) as cm:
self.resource.on_get(self.req, self.resp, self.tenant_keystone_id,
self.order.id)
exception = cm.exception
self.assertEqual(falcon.HTTP_404, exception.status)
def test_should_throw_exception_for_delete_when_order_not_found(self):
self.order_repo.get.side_effect = exception.NotFound(
self.order_repo.delete_entity_by_id.side_effect = excep.NotFound(
"Test not found exception")
with self.assertRaises(exception.NotFound):
with self.assertRaises(falcon.HTTPError) as cm:
self.resource.on_delete(self.req, self.resp,
self.tenant_keystone_id,
self.order.id)
exception = cm.exception
self.assertEqual(falcon.HTTP_404, exception.status)
if __name__ == '__main__':
unittest.main()