Merge "Handle ambiguous physical resource IDs"

This commit is contained in:
Jenkins 2016-12-13 07:43:24 +00:00 committed by Gerrit Code Review
commit 1190b1bead
9 changed files with 55 additions and 12 deletions

View File

@ -311,6 +311,7 @@ def map_remote_error(ex):
'PropertyUnspecifiedError',
'NotSupported',
'InvalidBreakPointHook',
'PhysicalResourceIDAmbiguity',
)
denied_errors = ('Forbidden', 'NotAuthorized')
already_exists_errors = ('StackExists')

View File

@ -63,6 +63,7 @@ class FaultWrapper(wsgi.Middleware):
'InvalidGlobalResource': webob.exc.HTTPInternalServerError,
'ResourceNotAvailable': webob.exc.HTTPNotFound,
'PhysicalResourceNameAmbiguity': webob.exc.HTTPBadRequest,
'PhysicalResourceIDAmbiguity': webob.exc.HTTPBadRequest,
'InvalidTenant': webob.exc.HTTPForbidden,
'Forbidden': webob.exc.HTTPForbidden,
'StackExists': webob.exc.HTTPConflict,

View File

@ -177,6 +177,11 @@ class PhysicalResourceNameAmbiguity(HeatException):
"Multiple physical resources were found with name (%(name)s).")
class PhysicalResourceIDAmbiguity(HeatException):
msg_fmt = _(
"Multiple resources were found with the physical ID (%(phys_id)s).")
class InvalidTenant(HeatException):
msg_fmt = _("Searching Tenant %(target)s "
"from Tenant %(actual)s forbidden.")

View File

@ -160,6 +160,11 @@ def resource_get_by_physical_resource_id(context, physical_resource_id):
physical_resource_id)
def resource_get_all_by_physical_resource_id(context, physical_resource_id):
return IMPL.resource_get_all_by_physical_resource_id(context,
physical_resource_id)
def stack_get(context, stack_id, show_deleted=False, eager_load=True):
return IMPL.stack_get(context, stack_id, show_deleted=show_deleted,
eager_load=eager_load)

View File

@ -204,7 +204,7 @@ def resource_get_by_name_and_stack(context, resource_name, stack_id):
return result
def resource_get_by_physical_resource_id(context, physical_resource_id):
def resource_get_all_by_physical_resource_id(context, physical_resource_id):
results = (context.session.query(models.Resource)
.filter_by(physical_resource_id=physical_resource_id)
.all())
@ -212,8 +212,16 @@ def resource_get_by_physical_resource_id(context, physical_resource_id):
for result in results:
if context is None or context.tenant_id in (
result.stack.tenant, result.stack.stack_user_project_id):
return result
return None
yield result
def resource_get_by_physical_resource_id(context, physical_resource_id):
results = resource_get_all_by_physical_resource_id(context,
physical_resource_id)
try:
return next(results)
except StopIteration:
return None
def resource_get_all(context):

View File

@ -1907,13 +1907,21 @@ class EngineService(service.ServiceBase):
:param cnxt: RPC context.
:param physical_resource_id: The physical resource ID to look up.
"""
rs = resource_objects.Resource.get_by_physical_resource_id(
rsrcs = resource_objects.Resource.get_all_by_physical_resource_id(
cnxt,
physical_resource_id)
if not rs:
if not rsrcs:
raise exception.EntityNotFound(entity='Resource',
name=physical_resource_id)
# This call is used only in the cfn API, which only cares about
# finding the stack anyway. So allow duplicate resource IDs within the
# same stack.
if len({rs.stack_id for rs in rsrcs}) > 1:
raise exception.PhysicalResourceIDAmbiguity(
phys_id=physical_resource_id)
rs = rsrcs[0]
stack = parser.Stack.load(cnxt, stack_id=rs.stack_id)
resource = stack[rs.name]

View File

@ -202,11 +202,12 @@ class Resource(
return cls._from_db_object(cls(context), context, resource_db)
@classmethod
def get_by_physical_resource_id(cls, context, physical_resource_id):
resource_db = db_api.resource_get_by_physical_resource_id(
def get_all_by_physical_resource_id(cls, context, physical_resource_id):
matches = db_api.resource_get_all_by_physical_resource_id(
context,
physical_resource_id)
return cls._from_db_object(cls(context), context, resource_db)
return [cls._from_db_object(cls(context), context, resource_db)
for resource_db in matches]
@classmethod
def update_by_id(cls, context, resource_id, values):

View File

@ -2318,6 +2318,20 @@ class DBAPIResourceTest(common.HeatTestCase):
self.assertIsNone(db_api.resource_get_by_physical_resource_id(self.ctx,
UUID2))
def test_resource_get_all_by_physical_resource_id(self):
create_resource(self.ctx, self.stack)
create_resource(self.ctx, self.stack)
ret_res = db_api.resource_get_all_by_physical_resource_id(self.ctx,
UUID1)
ret_list = list(ret_res)
self.assertTrue(ret_list)
for res in ret_list:
self.assertEqual(UUID1, res.physical_resource_id)
mt = db_api.resource_get_all_by_physical_resource_id(self.ctx, UUID2)
self.assertFalse(list(mt))
def test_resource_get_all(self):
values = [
{'name': 'res1'},

View File

@ -223,10 +223,10 @@ class SoftwareConfigServiceTest(common.HeatTestCase):
self.ctx, server_id=server.resource_id)
self.assertEqual([deployment], deployments)
rs = resource_objects.Resource.get_by_physical_resource_id(
rsrcs = resource_objects.Resource.get_all_by_physical_resource_id(
self.ctx, server_id)
self.assertEqual(deployment['config_id'],
rs.rsrc_metadata.get('deployments')[0]['id'])
rsrcs[0].rsrc_metadata.get('deployments')[0]['id'])
def test_metadata_software_deployments(self):
stack_name = 'test_metadata_software_deployments'
@ -271,9 +271,9 @@ class SoftwareConfigServiceTest(common.HeatTestCase):
# assert that metadata via metadata_software_deployments matches
# metadata via server resource
rs = resource_objects.Resource.get_by_physical_resource_id(
rsrcs = resource_objects.Resource.get_all_by_physical_resource_id(
self.ctx, server_id)
self.assertEqual(metadata, rs.rsrc_metadata.get('deployments'))
self.assertEqual(metadata, rsrcs[0].rsrc_metadata.get('deployments'))
deployments = self.engine.metadata_software_deployments(
self.ctx, server_id=str(uuid.uuid4()))