Regenerate context during targeting

We are only doing a copy.copy() on context when we do target_cell(),
which may mean we're sharing more across threads than we intend to.
This makes that a full regeneration of the context (like we would
do over RPC). This will necessarily dump the targeting, if already
set, and any per-thread database context.

Closes-Bug: #1722404
Change-Id: Id24dea7465bafc1f6f58c4a121c4ffb35b7a634a
(cherry picked from commit 42b70509c6)
This commit is contained in:
Dan Smith 2017-10-12 16:37:14 -07:00 committed by Matt Riedemann
parent e162f6c7ab
commit 42e5b7549e
2 changed files with 28 additions and 1 deletions

View File

@ -425,7 +425,14 @@ def target_cell(context, cell_mapping):
:param context: The RequestContext to add connection information
:param cell_mapping: An objects.CellMapping object or None
"""
cctxt = copy.copy(context)
# Create a sanitized copy of context by serializing and deserializing it
# (like we would do over RPC). This help ensure that we have a clean
# copy of the context with all the tracked attributes, but without any
# of the hidden/private things we cache on a context. We do this to avoid
# unintentional sharing of cached thread-local data across threads.
# Specifically, this won't include any oslo_db-set transaction context, or
# any existing cell targeting.
cctxt = RequestContext.from_dict(context.to_dict())
set_target_cell(cctxt, cell_mapping)
yield cctxt

View File

@ -274,6 +274,26 @@ class ContextTestCase(test.NoDBTestCase):
self.assertEqual(mock.sentinel.db_conn, ctxt.db_connection)
self.assertEqual(mock.sentinel.mq_conn, ctxt.mq_connection)
@mock.patch('nova.context.set_target_cell')
def test_target_cell_regenerates(self, mock_set):
ctxt = context.RequestContext('fake', 'fake')
# Set a non-tracked property on the context to make sure it
# does not make it to the targeted one (like a copy would do)
ctxt.sentinel = mock.sentinel.parent
with context.target_cell(ctxt, mock.sentinel.cm) as cctxt:
# Should be a different object
self.assertIsNot(cctxt, ctxt)
# Should not have inherited the non-tracked property
self.assertFalse(hasattr(cctxt, 'sentinel'),
'Targeted context was copied from original')
# Set another non-tracked property
cctxt.sentinel = mock.sentinel.child
# Make sure we didn't pollute the original context
self.assertNotEqual(ctxt.sentinel, mock.sentinel.child)
def test_get_context(self):
ctxt = context.get_context()
self.assertIsNone(ctxt.user_id)