Skip to content

Commit

Permalink
Check context before returning cached value
Browse files Browse the repository at this point in the history
The key manager caches the value of barbican client to be reused,
saving an extra call to keystone.  The cached value is only
applicable to the current context, so the context must be checked
before returning the cached value.

Closes-Bug: #1523646

Change-Id: I7cd7f1ba8a749b230c611e4fb20ccf4127354c35
  • Loading branch information
dave-mccowan committed Dec 14, 2015
1 parent 3f8c69b commit 676a53c
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 42 deletions.
92 changes: 51 additions & 41 deletions nova/keymgr/barbican.py
Expand Up @@ -62,6 +62,7 @@ class BarbicanKeyManager(key_mgr.KeyManager):

def __init__(self):
self._barbican_client = None
self._current_context = None
self._base_url = None

def _get_barbican_client(self, ctxt):
Expand All @@ -72,47 +73,56 @@ def _get_barbican_client(self, ctxt):
:raises Forbidden: if the ctxt is None
"""

if not self._barbican_client:
# Confirm context is provided, if not raise forbidden
if not ctxt:
msg = _("User is not authorized to use key manager.")
LOG.error(msg)
raise exception.Forbidden(msg)

try:
_SESSION = session.Session.load_from_conf_options(
CONF,
BARBICAN_OPT_GROUP)

auth = ctxt.get_auth_plugin()
service_type, service_name, interface = (CONF.
barbican.
catalog_info.
split(':'))
region_name = CONF.barbican.os_region_name
service_parameters = {'service_type': service_type,
'service_name': service_name,
'interface': interface,
'region_name': region_name}

if CONF.barbican.endpoint_template:
self._base_url = (CONF.barbican.endpoint_template %
ctxt.to_dict())
else:
self._base_url = _SESSION.get_endpoint(
auth, **service_parameters)

# the barbican endpoint can't have the '/v1' on the end
self._barbican_endpoint = self._base_url.rpartition('/')[0]

sess = session.Session(auth=auth)
self._barbican_client = barbican_client.Client(
session=sess,
endpoint=self._barbican_endpoint)

except Exception as e:
with excutils.save_and_reraise_exception():
LOG.error(_LE("Error creating Barbican client: %s"), e)
# Confirm context is provided, if not raise forbidden
if not ctxt:
msg = _("User is not authorized to use key manager.")
LOG.error(msg)
raise exception.Forbidden(msg)

if not hasattr(ctxt, 'project_id') or ctxt.project_id is None:
msg = _("Unable to create Barbican Client without project_id.")
LOG.error(msg)
raise exception.KeyManagerError(msg)

# If same context, return cached barbican client
if self._barbican_client and self._current_context == ctxt:
return self._barbican_client

try:
_SESSION = session.Session.load_from_conf_options(
CONF,
BARBICAN_OPT_GROUP)

auth = ctxt.get_auth_plugin()
service_type, service_name, interface = (CONF.
barbican.
catalog_info.
split(':'))
region_name = CONF.barbican.os_region_name
service_parameters = {'service_type': service_type,
'service_name': service_name,
'interface': interface,
'region_name': region_name}

if CONF.barbican.endpoint_template:
self._base_url = (CONF.barbican.endpoint_template %
ctxt.to_dict())
else:
self._base_url = _SESSION.get_endpoint(
auth, **service_parameters)

# the barbican endpoint can't have the '/v1' on the end
self._barbican_endpoint = self._base_url.rpartition('/')[0]

sess = session.Session(auth=auth)
self._barbican_client = barbican_client.Client(
session=sess,
endpoint=self._barbican_endpoint)
self._current_context = ctxt

except Exception as e:
with excutils.save_and_reraise_exception():
LOG.error(_LE("Error creating Barbican client: %s"), e)

return self._barbican_client

Expand Down
52 changes: 51 additions & 1 deletion nova/tests/unit/keymgr/test_barbican.py
Expand Up @@ -37,8 +37,9 @@ def setUp(self):
super(BarbicanKeyManagerTestCase, self).setUp()

# Create fake auth_token
self.ctxt = mock.Mock()
self.ctxt = mock.MagicMock()
self.ctxt.auth_token = "fake_token"
self.ctxt.project = "fake_project"

# Create mock barbican client
self._build_mock_barbican()
Expand All @@ -49,6 +50,7 @@ def setUp(self):
self.pre_hex = "AIDxQp2++uAbKaTVDMXFYIu8PIugJGqkK0JLqkU0rhY="
self.hex = ("0080f1429dbefae01b29a4d50cc5c5608bbc3c8ba0246aa42b424baa4"
"534ae16")
self.key_mgr._current_context = self.ctxt
self.key_mgr._base_url = "http://host:9311/v1"
self.addCleanup(self._restore)

Expand Down Expand Up @@ -221,3 +223,51 @@ def test_store_null_context(self):
self.key_mgr._barbican_client = None
self.assertRaises(exception.Forbidden,
self.key_mgr.store_key, None, None)

@mock.patch('keystoneclient.session.Session')
@mock.patch('barbicanclient.client.Client')
def test_get_barbican_client_new(self, mock_barbican, mock_keystone):
manager = self._create_key_manager()
manager._get_barbican_client(self.ctxt)
self.assertEqual(mock_keystone.call_count, 1)
self.assertEqual(mock_barbican.call_count, 1)

@mock.patch('keystoneclient.session.Session')
@mock.patch('barbicanclient.client.Client')
def test_get_barbican_client_reused(self, mock_barbican, mock_keystone):
manager = self._create_key_manager()
manager._get_barbican_client(self.ctxt)
self.assertEqual(mock_keystone.call_count, 1)
self.assertEqual(mock_barbican.call_count, 1)
manager._get_barbican_client(self.ctxt)
self.assertEqual(mock_keystone.call_count, 1)
self.assertEqual(mock_barbican.call_count, 1)

@mock.patch('keystoneclient.session.Session')
@mock.patch('barbicanclient.client.Client')
def test_get_barbican_client_not_reused(self, mock_barbican,
mock_keystone):
manager = self._create_key_manager()
manager._get_barbican_client(self.ctxt)
self.assertEqual(mock_keystone.call_count, 1)
self.assertEqual(mock_barbican.call_count, 1)
ctxt2 = mock.MagicMock()
ctxt2.auth_token = "fake_token2"
ctxt2.project = "fake_project2"
manager._get_barbican_client(ctxt2)
self.assertEqual(mock_keystone.call_count, 2)
self.assertEqual(mock_barbican.call_count, 2)

def test_get_barbican_client_null_context(self):
self.assertRaises(exception.Forbidden,
self.key_mgr._get_barbican_client, None)

def test_get_barbican_client_missing_project(self):
del(self.ctxt.project_id)
self.assertRaises(exception.KeyManagerError,
self.key_mgr._get_barbican_client, self.ctxt)

def test_get_barbican_client_none_project(self):
self.ctxt.project_id = None
self.assertRaises(exception.KeyManagerError,
self.key_mgr._get_barbican_client, self.ctxt)

0 comments on commit 676a53c

Please sign in to comment.