barbican/barbican/tests/queue/test_keystone_listener.py

336 lines
15 KiB
Python

# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from oslo_utils import uuidutils
import mock
import oslo_messaging
from oslo_service import service
from barbican.common import config
from barbican import queue
from barbican.queue import keystone_listener
from barbican.tasks import keystone_consumer as consumer
from barbican.tests import utils
class UtilMixin(object):
def __init__(self, *args, **kwargs):
super(UtilMixin, self).__init__(*args, **kwargs)
self.conf = config.CONF
# dict which has item as {property: (value, group_name)}
self.overrides = {}
def revert_overrides(self):
'''Reverts configuration override values after test end.'''
for k, v in self.overrides.items():
value, group = v
self.conf.set_override(k, value, group)
def setUp(self):
super(UtilMixin, self).setUp()
self.addCleanup(self.revert_overrides)
def opt_in_group(self, group, **kw):
for k, v in kw.items():
# add to local overrides if its not already set
# we want to keep the original value from first override
dict_value = self.overrides.get(k)
if not dict_value:
if group:
orig_value = getattr(getattr(self.conf, group), k)
else:
orig_value = getattr(self.conf, k)
self.overrides[k] = orig_value, group
self.conf.set_override(k, v, group)
class WhenUsingNotificationTask(UtilMixin, utils.BaseTestCase):
"""Test for 'Notification' task functionality."""
def setUp(self):
super(WhenUsingNotificationTask, self).setUp()
self.task = keystone_listener.NotificationTask(self.conf)
self.payload = {'resource_info': uuidutils.generate_uuid(
dashed=False)}
self.type_index = 2
self.payload_index = 3
self.task_args = ['my_context', 'publisher_id', 'event_type',
self.payload, {'metadata': 'value'}]
@mock.patch.object(keystone_listener.NotificationTask, 'process_event')
def test_info_level_notification(self, mock_process):
self.task.info(*self.task_args)
mock_process.assert_called_once_with(*self.task_args)
@mock.patch.object(consumer.KeystoneEventConsumer, 'process',
return_value=None)
def test_create_project_event_notification(self, mock_process):
self.task_args[self.type_index] = 'identity.project.created'
result = self.task.info(*self.task_args)
self.assertFalse(mock_process.called, 'Should not call event consumer '
'for project create event')
self.assertIsNone(result)
@mock.patch.object(consumer.KeystoneEventConsumer, 'process',
return_value=None)
def test_update_project_event_notification(self, mock_process):
self.task_args[self.type_index] = 'identity.project.updated'
result = self.task.info(*self.task_args)
self.assertFalse(mock_process.called, 'Should not call event consumer '
'for project update event')
self.assertIsNone(result)
@mock.patch.object(consumer.KeystoneEventConsumer, 'process',
return_value=None)
def test_delete_project_event_notification_with_required_data(
self, mock_process):
project_id = uuidutils.generate_uuid(dashed=False)
self.task_args[self.type_index] = 'identity.project.deleted'
self.task_args[self.payload_index] = {'resource_info': project_id}
result = self.task.info(*self.task_args)
mock_process.assert_called_once_with(project_id=project_id,
operation_type='deleted',
resource_type='project')
self.assertEqual(oslo_messaging.NotificationResult.HANDLED, result)
@mock.patch.object(consumer.KeystoneEventConsumer, 'process',
return_value=None)
def test_delete_project_event_with_different_service_name_in_event_type(
self, mock_process):
project_id = uuidutils.generate_uuid(dashed=False)
self.task_args[self.type_index] = 'aaa.project.deleted'
self.task_args[self.payload_index] = {'resource_info': project_id}
result = self.task.info(*self.task_args)
mock_process.assert_called_once_with(project_id=project_id,
operation_type='deleted',
resource_type='project')
self.assertEqual(oslo_messaging.NotificationResult.HANDLED, result)
@mock.patch.object(consumer.KeystoneEventConsumer, 'process',
return_value=None)
def test_delete_project_event_with_event_type_in_different_case(
self, mock_process):
project_id = uuidutils.generate_uuid(dashed=False)
self.task_args[self.type_index] = 'Identity.PROJECT.DeleteD'
self.task_args[self.payload_index] = {'resource_info': project_id}
result = self.task.info(*self.task_args)
mock_process.assert_called_once_with(project_id=project_id,
operation_type='deleted',
resource_type='project')
self.assertEqual(oslo_messaging.NotificationResult.HANDLED, result)
@mock.patch.object(consumer.KeystoneEventConsumer, 'process',
return_value=None)
def test_delete_project_event_with_incomplete_event_type_format(
self, mock_process):
project_id = uuidutils.generate_uuid(dashed=False)
self.task_args[self.type_index] = 'project.deleted'
self.task_args[self.payload_index] = {'resource_info': project_id}
result = self.task.info(*self.task_args)
self.assertFalse(mock_process.called, 'Should not call event consumer '
'for project delete event as service name is missing '
'in event_type data. Expected format is '
' <service_name>.<resource_name>.<operation_type>')
self.assertIsNone(result)
@mock.patch.object(consumer.KeystoneEventConsumer, 'process',
return_value=None)
def test_delete_project_event_notification_with_missing_resource_info(
self, mock_process):
self.task_args[self.type_index] = 'identity.project.deleted'
self.task_args[self.payload_index] = {'resource_info': None}
result = self.task.info(*self.task_args)
self.assertFalse(mock_process.called, 'Should not call event consumer '
'for project delete event when project_id is missing '
'in payload')
self.assertIsNone(result)
@mock.patch.object(consumer.KeystoneEventConsumer, 'process',
return_value=None)
def test_delete_project_event_notification_with_missing_payload(
self, mock_process):
self.task_args[self.type_index] = 'identity.project.deleted'
self.task_args[self.payload_index] = None
result = self.task.info(*self.task_args)
self.assertFalse(mock_process.called, 'Should not call event consumer '
'for project delete event when payload is missing')
self.assertIsNone(result)
@mock.patch.object(consumer.KeystoneEventConsumer, 'process',
return_value=None)
def test_delete_project_event_notification_with_blank_payload(
self, mock_process):
self.task_args[self.type_index] = 'identity.project.deleted'
self.task_args[self.payload_index] = ''
result = self.task.info(*self.task_args)
self.assertFalse(mock_process.called, 'Should not call event consumer '
'for project delete event when payload is missing')
self.assertIsNone(result)
@mock.patch.object(consumer.KeystoneEventConsumer, 'process',
return_value=None)
def test_event_notification_with_missing_event_type(self, mock_process):
project_id = uuidutils.generate_uuid(dashed=False)
self.task_args[self.type_index] = None
self.task_args[self.payload_index] = {'resource_info': project_id}
result = self.task.info(*self.task_args)
self.assertFalse(mock_process.called, 'Should not call event consumer '
'for keystone event when event_type is missing in '
'notification')
self.assertIsNone(result)
@mock.patch.object(consumer.KeystoneEventConsumer, 'process',
return_value=None)
def test_event_notification_with_blank_event_type(self, mock_process):
project_id = uuidutils.generate_uuid(dashed=False)
self.task_args[self.type_index] = ''
self.task_args[self.payload_index] = {'resource_info': project_id}
result = self.task.info(*self.task_args)
self.assertFalse(mock_process.called, 'Should not call event consumer '
'keystone event when event_type is blank in '
'notification')
self.assertIsNone(result)
@mock.patch.object(consumer.KeystoneEventConsumer, 'process')
def test_event_notification_with_processing_error_requeue_disabled(
self, mock_process):
self.opt_in_group(queue.KS_NOTIFICATIONS_GRP_NAME, allow_requeue=False)
local_task = keystone_listener.NotificationTask(self.conf)
mock_process.side_effect = Exception('Dummy Error')
project_id = uuidutils.generate_uuid(dashed=False)
self.task_args[self.type_index] = 'identity.project.deleted'
self.task_args[self.payload_index] = {'resource_info': project_id}
result = local_task.info(*self.task_args)
self.assertTrue(mock_process.called, 'Should call event consumer for'
' project delete event')
self.assertEqual(oslo_messaging.NotificationResult.HANDLED, result)
@mock.patch.object(consumer.KeystoneEventConsumer, 'process')
def test_event_notification_with_processing_error_requeue_enabled(
self, mock_process):
self.opt_in_group(queue.KS_NOTIFICATIONS_GRP_NAME, allow_requeue=True)
local_task = keystone_listener.NotificationTask(self.conf)
mock_process.side_effect = Exception('Dummy Error')
project_id = uuidutils.generate_uuid(dashed=False)
self.task_args[self.type_index] = 'identity.project.deleted'
self.task_args[self.payload_index] = {'resource_info': project_id}
result = local_task.info(*self.task_args)
self.assertTrue(mock_process.called, 'Should call event consumer for'
' project delete event')
self.assertEqual(oslo_messaging.NotificationResult.REQUEUE, result)
class WhenUsingMessageServer(UtilMixin, utils.BaseTestCase):
"""Test using the asynchronous task client."""
def setUp(self):
super(WhenUsingMessageServer, self).setUp()
queue.init(self.conf)
patcher = mock.patch('oslo_messaging.notify.listener.'
'NotificationServer')
mock_server_class = patcher.start()
self.addCleanup(patcher.stop)
self.msg_server_mock = mock_server_class()
self.msg_server_mock.start.return_value = None
self.msg_server_mock.stop.return_value = None
self.msg_server_mock.wait.return_value = None
@mock.patch.object(queue, 'get_notification_server')
@mock.patch.object(queue, 'get_notification_target')
def test_target_and_notification_server_invocations(self, mock_target,
mock_server):
target = 'a target value here'
mock_target.return_value = target
msg_server = keystone_listener.MessageServer(self.conf)
mock_target.assert_called_once_with()
mock_server.assert_called_once_with(
targets=[target], endpoints=[msg_server])
def test_keystone_notification_config_used(self):
topic = 'my test topic'
exchange = 'my test exchange'
version = ' my test version'
self.opt_in_group(queue.KS_NOTIFICATIONS_GRP_NAME, topic=topic)
self.opt_in_group(queue.KS_NOTIFICATIONS_GRP_NAME,
control_exchange=exchange)
self.opt_in_group(queue.KS_NOTIFICATIONS_GRP_NAME, version=version)
self.opt_in_group(queue.KS_NOTIFICATIONS_GRP_NAME, version=version)
target = queue.get_notification_target()
self.assertEqual(topic, target.topic)
self.assertEqual(exchange, target.exchange)
self.assertEqual(version, target.version)
@mock.patch.object(service.Service, '__init__')
def test_keystone_notification_pool_size_used(self, mock_service_init):
thread_pool_size = 5
self.opt_in_group(queue.KS_NOTIFICATIONS_GRP_NAME,
thread_pool_size=thread_pool_size)
msg_server = keystone_listener.MessageServer(self.conf)
mock_service_init.assert_called_once_with(msg_server,
threads=thread_pool_size)
@mock.patch.object(service.Service, 'start')
def test_should_start(self, mock_service):
msg_server = keystone_listener.MessageServer(self.conf)
msg_server.start()
self.msg_server_mock.start.assert_called_with()
@mock.patch.object(service.Service, 'stop', autospec=True)
def test_should_stop(self, mock_service_stop):
msg_server = keystone_listener.MessageServer(self.conf)
msg_server.stop()
self.msg_server_mock.stop.assert_called_with()
@mock.patch.object(service.Service, 'wait')
def test_should_wait(self, mock_service_wait):
msg_server = keystone_listener.MessageServer(self.conf)
msg_server.wait()
self.assertFalse(self.msg_server_mock.stop.called, 'No need to call'
'message server wait() as Service itself creates the '
' wait event')
self.assertTrue(mock_service_wait.called, 'Expected to only call '
'service.Service.wait() method')