Custom constrain for keystone service

Adds custom constrain for keystone service.

implements blueprint keystone-resource-service-endpoint

Change-Id: I6a2145ab51688292eb732e578db87fb993d2804e
This commit is contained in:
Kanagaraj Manickam 2015-04-29 12:13:24 +05:30
parent d7cadf02c9
commit 384d82e04c
4 changed files with 155 additions and 6 deletions

View File

@ -69,6 +69,20 @@ class KeystoneClientPlugin(keystone.KeystoneClientPlugin):
raise exceptions.KeystoneGroupNotFound(group_id=group) raise exceptions.KeystoneGroupNotFound(group_id=group)
def get_service_id(self, service):
try:
service_obj = self.client().client.services.get(service)
return service_obj.id
except keystone_exceptions.NotFound:
service_list = self.client().client.services.list(name=service)
if len(service_list) == 1:
return service_list[0].id
elif len(service_list) > 1:
raise exceptions.KeystoneServiceNameConflict(service=service)
else:
raise exceptions.KeystoneServiceNotFound(service_id=service)
class KeystoneRoleConstraint(constraints.BaseCustomConstraint): class KeystoneRoleConstraint(constraints.BaseCustomConstraint):
@ -100,3 +114,12 @@ class KeystoneGroupConstraint(constraints.BaseCustomConstraint):
def validate_with_client(self, client, group): def validate_with_client(self, client, group):
client.client_plugin('keystone').get_group_id(group) client.client_plugin('keystone').get_group_id(group)
class KeystoneServiceConstraint(constraints.BaseCustomConstraint):
expected_exceptions = (exceptions.KeystoneServiceNotFound,
exceptions.KeystoneServiceNameConflict,)
def validate_with_client(self, client, service):
client.client_plugin('keystone').get_service_id(service)

View File

@ -29,3 +29,12 @@ class KeystoneDomainNotFound(exception.HeatException):
class KeystoneGroupNotFound(exception.HeatException): class KeystoneGroupNotFound(exception.HeatException):
msg_fmt = _("Keystone group %(group_id)s does not found") msg_fmt = _("Keystone group %(group_id)s does not found")
class KeystoneServiceNotFound(exception.HeatException):
msg_fmt = _("Keystone service %(service_id)s does not found")
class KeystoneServiceNameConflict(exception.HeatException):
msg_fmt = _("Keystone has more than one service with same name "
"%(service)s. Please use service id instead of name")

View File

@ -12,13 +12,16 @@
# under the License. # under the License.
import mock import mock
import testtools import six
from keystoneclient import exceptions as keystone_exceptions
from .. import client # noqa from .. import client # noqa
from .. import exceptions # noqa from .. import exceptions # noqa
from heat.tests import common
class KeystoneRoleConstraintTest(testtools.TestCase): class KeystoneRoleConstraintTest(common.HeatTestCase):
def test_expected_exceptions(self): def test_expected_exceptions(self):
self.assertEqual((exceptions.KeystoneRoleNotFound,), self.assertEqual((exceptions.KeystoneRoleNotFound,),
@ -38,7 +41,7 @@ class KeystoneRoleConstraintTest(testtools.TestCase):
client_plugin_mock.get_role_id.assert_called_once_with('role_1') client_plugin_mock.get_role_id.assert_called_once_with('role_1')
class KeystoneProjectConstraintTest(testtools.TestCase): class KeystoneProjectConstraintTest(common.HeatTestCase):
def test_expected_exceptions(self): def test_expected_exceptions(self):
self.assertEqual((exceptions.KeystoneProjectNotFound,), self.assertEqual((exceptions.KeystoneProjectNotFound,),
@ -58,7 +61,7 @@ class KeystoneProjectConstraintTest(testtools.TestCase):
client_plugin_mock.get_project_id.assert_called_once_with('project_1') client_plugin_mock.get_project_id.assert_called_once_with('project_1')
class KeystoneGroupConstraintTest(testtools.TestCase): class KeystoneGroupConstraintTest(common.HeatTestCase):
def test_expected_exceptions(self): def test_expected_exceptions(self):
self.assertEqual((exceptions.KeystoneGroupNotFound,), self.assertEqual((exceptions.KeystoneGroupNotFound,),
@ -78,7 +81,7 @@ class KeystoneGroupConstraintTest(testtools.TestCase):
client_plugin_mock.get_group_id.assert_called_once_with('group_1') client_plugin_mock.get_group_id.assert_called_once_with('group_1')
class KeystoneDomainConstraintTest(testtools.TestCase): class KeystoneDomainConstraintTest(common.HeatTestCase):
def test_expected_exceptions(self): def test_expected_exceptions(self):
self.assertEqual((exceptions.KeystoneDomainNotFound,), self.assertEqual((exceptions.KeystoneDomainNotFound,),
@ -96,3 +99,117 @@ class KeystoneDomainConstraintTest(testtools.TestCase):
'domain_1')) 'domain_1'))
client_plugin_mock.get_domain_id.assert_called_once_with('domain_1') client_plugin_mock.get_domain_id.assert_called_once_with('domain_1')
class KeystoneServiceConstraintTest(common.HeatTestCase):
sample_uuid = '477e8273-60a7-4c41-b683-fdb0bc7cd151'
def test_expected_exceptions(self):
self.assertEqual((exceptions.KeystoneServiceNotFound,
exceptions.KeystoneServiceNameConflict,),
client.KeystoneServiceConstraint.expected_exceptions,
"KeystoneServiceConstraint expected exceptions error")
def test_constrain(self):
constrain = client.KeystoneServiceConstraint()
client_mock = mock.MagicMock()
client_plugin_mock = mock.MagicMock()
client_plugin_mock.get_service_id.return_value = self.sample_uuid
client_mock.client_plugin.return_value = client_plugin_mock
self.assertIsNone(constrain.validate_with_client(client_mock,
self.sample_uuid))
client_plugin_mock.get_service_id.assert_called_once_with(
self.sample_uuid
)
class KeystoneClientPluginServiceTest(common.HeatTestCase):
sample_uuid = '477e8273-60a7-4c41-b683-fdb0bc7cd152'
sample_name = 'sample_service'
def _get_mock_service(self):
srv = mock.MagicMock()
srv.id = self.sample_uuid
srv.name = self.sample_name
return srv
def setUp(self):
super(KeystoneClientPluginServiceTest, self).setUp()
self._client = mock.MagicMock()
self._client.client = mock.MagicMock()
self._client.client.services = mock.MagicMock()
@mock.patch.object(client.KeystoneClientPlugin, 'client')
def test_get_service_id(self, client_keystone):
self._client.client.services.get.return_value = (self
._get_mock_service())
client_keystone.return_value = self._client
client_plugin = client.KeystoneClientPlugin(
context=mock.MagicMock()
)
self.assertEqual(self.sample_uuid,
client_plugin.get_service_id(self.sample_uuid))
@mock.patch.object(client.KeystoneClientPlugin, 'client')
def test_get_service_id_with_name(self, client_keystone):
self._client.client.services.get.side_effect = (keystone_exceptions
.NotFound)
self._client.client.services.list.return_value = [
self._get_mock_service()
]
client_keystone.return_value = self._client
client_plugin = client.KeystoneClientPlugin(
context=mock.MagicMock()
)
self.assertEqual(self.sample_uuid,
client_plugin.get_service_id(self.sample_name))
@mock.patch.object(client.KeystoneClientPlugin, 'client')
def test_get_service_id_with_name_conflict(self, client_keystone):
self._client.client.services.get.side_effect = (keystone_exceptions
.NotFound)
self._client.client.services.list.return_value = [
self._get_mock_service(),
self._get_mock_service()
]
client_keystone.return_value = self._client
client_plugin = client.KeystoneClientPlugin(
context=mock.MagicMock()
)
ex = self.assertRaises(exceptions.KeystoneServiceNameConflict,
client_plugin.get_service_id,
self.sample_name)
msg = ("Keystone has more than one service with same name "
"%s. Please use service id instead of name" %
self.sample_name)
self.assertEqual(msg, six.text_type(ex))
@mock.patch.object(client.KeystoneClientPlugin, 'client')
def test_get_service_id_not_found(self, client_keystone):
self._client.client.services.get.side_effect = (keystone_exceptions
.NotFound)
self._client.client.services.list.return_value = [
]
client_keystone.return_value = self._client
client_plugin = client.KeystoneClientPlugin(
context=mock.MagicMock()
)
ex = self.assertRaises(exceptions.KeystoneServiceNotFound,
client_plugin.get_service_id,
self.sample_name)
msg = ("Keystone service %s does not found" %
self.sample_name)
self.assertEqual(msg, six.text_type(ex))

View File

@ -1,6 +1,5 @@
[metadata] [metadata]
name = heat-contrib-keystone name = heat-contrib-keystone
version = 0.1
summary = Heat resources for Keystone summary = Heat resources for Keystone
description-file = description-file =
README.md README.md
@ -35,6 +34,7 @@ heat.constraints =
keystone.domain=heat_keystone.client:KeystoneDomainConstraint keystone.domain=heat_keystone.client:KeystoneDomainConstraint
keystone.project=heat_keystone.client:KeystoneProjectConstraint keystone.project=heat_keystone.client:KeystoneProjectConstraint
keystone.group=heat_keystone.client:KeystoneGroupConstraint keystone.group=heat_keystone.client:KeystoneGroupConstraint
keystone.service=heat_keystone.client:KeystoneServiceConstraint
[global] [global]
setup-hooks = setup-hooks =