Fix failing namespace list delete race
If a namespace is deleted by another client while we are doing a namespace list operation, we will fail the list with NotFound if we try to pull the resource_type_associations list. The latter re-queries the DB for the namespace and will raise NotFound to us. This is especially bad because the namespace being deleted need not even belong to the caller of the list, as is the case in a tempest run. This makes us catch the failure and continue the operation, reporting no associations so that the client gets a consistent view and no error. Closes-Bug: #1973631 Change-Id: I09fc9164a08f42507d2aec44c5b382a72f232571
This commit is contained in:
parent
d7fa7a0321
commit
35e6c57bc9
|
@ -98,7 +98,13 @@ class NamespaceController(object):
|
|||
# Get resource type associations
|
||||
filters = dict()
|
||||
filters['namespace'] = db_namespace.namespace
|
||||
repo_rs_type_list = rs_repo.list(filters=filters)
|
||||
try:
|
||||
repo_rs_type_list = rs_repo.list(filters=filters)
|
||||
except exception.NotFound:
|
||||
# NOTE(danms): If we fail to list resource_types
|
||||
# for this namespace, do not fail the entire
|
||||
# namespace list operation with NotFound.
|
||||
repo_rs_type_list = []
|
||||
resource_type_list = [
|
||||
ResourceTypeAssociation.to_wsme_model(
|
||||
resource_type
|
||||
|
|
|
@ -18,6 +18,7 @@ from unittest import mock
|
|||
|
||||
from oslo_serialization import jsonutils
|
||||
import webob
|
||||
import wsme
|
||||
|
||||
from glance.api import policy
|
||||
from glance.api.v2 import metadef_namespaces as namespaces
|
||||
|
@ -296,6 +297,44 @@ class TestMetadefsControllers(base.IsolatedUnitTest):
|
|||
expected = set([NAMESPACE1, NAMESPACE3])
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_namespace_index_resource_type_delete_race(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
filters = {'resource_types': [RESOURCE_TYPE1]}
|
||||
|
||||
real_gmrtr = self.namespace_controller.gateway.\
|
||||
get_metadef_resource_type_repo
|
||||
|
||||
def race_delete(*a, **k):
|
||||
self.db.metadef_namespace_delete(request.context, NAMESPACE3)
|
||||
return real_gmrtr(*a, **k)
|
||||
|
||||
with mock.patch.object(self.namespace_controller.gateway,
|
||||
'get_metadef_resource_type_repo') as g:
|
||||
# NOTE(danms): We simulate a late delete of one of our
|
||||
# namespaces by hijacking the call to get the metadef RT
|
||||
# repo and doing a delete at that point, before we iterate
|
||||
# the list of namespaces we already pulled from the DB. If
|
||||
# the code in the index API method changes, this will need
|
||||
# to be updated.
|
||||
g.side_effect = race_delete
|
||||
output = self.namespace_controller.index(request, filters=filters)
|
||||
output = output.to_dict()
|
||||
self.assertEqual(2, len(output['namespaces']))
|
||||
actual = set([namespace.namespace for namespace
|
||||
in output['namespaces']])
|
||||
# We should still see both namespaces
|
||||
expected = set([NAMESPACE1, NAMESPACE3])
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
# And the first (undeleted) one should have the expected
|
||||
# associations...
|
||||
self.assertEqual(
|
||||
1, len(output['namespaces'][0].resource_type_associations))
|
||||
# ...but the one we deleted should be empty
|
||||
self.assertEqual(
|
||||
wsme.types.Unset,
|
||||
output['namespaces'][1].resource_type_associations)
|
||||
|
||||
def test_namespace_show(self):
|
||||
request = unit_test_utils.get_fake_request()
|
||||
output = self.namespace_controller.show(request, NAMESPACE1)
|
||||
|
|
Loading…
Reference in New Issue