Allow multiple required_service_extension

In some cases, some resources may require more than
one service_extension. In this case, we should allow
resources to define a list of required resources.

Change-Id: Iee0104a741cc050047824d23b8ab5ee9871c4f28
This commit is contained in:
Brendan Shephard 2022-04-13 14:07:56 +10:00 committed by bshephar
parent 380d087067
commit 5e14163f9c
4 changed files with 112 additions and 8 deletions

View File

@ -16,6 +16,7 @@ import contextlib
import datetime as dt
import itertools
import pydoc
import re
import tenacity
import weakref
@ -807,16 +808,21 @@ class Resource(status.ResourceStatus):
service_name=cls.default_client_name)
if endpoint_exists:
req_extension = cls.required_service_extension
if not req_extension:
return(True, None)
if isinstance(req_extension, str):
req_extension = re.split(' |,', req_extension)
for ext in req_extension:
is_ext_available = (
not req_extension or client_plugin.has_extension(
req_extension))
if is_ext_available:
return (True, None)
else:
client_plugin.has_extension(ext))
if not is_ext_available:
reason = _('Required extension {0} in {1} service '
'is not available.')
reason = reason.format(req_extension,
reason = reason.format(ext,
cls.default_client_name)
break
if is_ext_available:
return (True, None)
else:
reason = _('{0} {1} endpoint is not in service catalog.')
reason = reason.format(cls.default_client_name, service_type)

View File

@ -317,6 +317,20 @@ class ResourceWithDefaultClientNameExt(resource.Resource):
properties_schema = {}
class ResourceWithDefaultClientNameMultiStrExt(resource.Resource):
default_client_name = 'sample'
required_service_extension = 'foo,bar'
properties_schema = {}
class ResourceWithDefaultClientNameMultiExt(resource.Resource):
default_client_name = 'sample'
required_service_extension = ['foo', 'bar']
properties_schema = {}
class ResourceWithFnGetAttType(GenericResource):
def get_attribute(self, name):
pass

View File

@ -4064,6 +4064,84 @@ class ResourceAvailabilityTest(common.HeatTestCase):
self.assertRaises(MyException, res.handle_delete)
@mock.patch.object(clients.OpenStackClients, 'client_plugin')
def test_service_deployed_required_extension_true_string(
self,
mock_client_plugin_method):
"""Test availability of resource with a required extension. """
mock_service_types, mock_client_plugin = self._mock_client_plugin(
['test_type']
)
mock_client_plugin.has_extension = mock.Mock(
side_effect=[True, True])
mock_client_plugin_method.return_value = mock_client_plugin
rsrc = generic_rsrc.ResourceWithDefaultClientNameMultiStrExt
rsrc.is_service_available(
context=mock.Mock())[0]
mock_client_plugin_method.assert_called_once_with(
generic_rsrc.ResourceWithDefaultClientName.default_client_name)
mock_service_types.assert_called_once_with()
mock_client_plugin.does_endpoint_exist.assert_called_once_with(
service_type='test_type',
service_name=(generic_rsrc.ResourceWithDefaultClientName
.default_client_name))
mock_client_plugin.has_extension.has_calls(
[('foo'), ('bar')])
@mock.patch.object(clients.OpenStackClients, 'client_plugin')
def test_service_deployed_required_extension_true_list(
self,
mock_client_plugin_method):
"""Test availability of resource with a required extension. """
mock_service_types, mock_client_plugin = self._mock_client_plugin(
['test_type']
)
mock_client_plugin.has_extension = mock.Mock(
side_effect=[True, True])
mock_client_plugin_method.return_value = mock_client_plugin
rsrc = generic_rsrc.ResourceWithDefaultClientNameMultiExt
rsrc.is_service_available(
context=mock.Mock())[0]
mock_client_plugin_method.assert_called_once_with(
generic_rsrc.ResourceWithDefaultClientName.default_client_name)
mock_service_types.assert_called_once_with()
mock_client_plugin.does_endpoint_exist.assert_called_once_with(
service_type='test_type',
service_name=(generic_rsrc.ResourceWithDefaultClientName
.default_client_name))
mock_client_plugin.has_extension.has_calls(
[('foo'), ('bar')])
@mock.patch.object(clients.OpenStackClients, 'client_plugin')
def test_service_deployed_required_extension_true_list_fail(
self,
mock_client_plugin_method):
"""Test availability of resource with a required extension. """
mock_service_types, mock_client_plugin = self._mock_client_plugin(
['test_type']
)
mock_client_plugin.has_extension = mock.Mock(
side_effect=[True, False])
mock_client_plugin_method.return_value = mock_client_plugin
rsrc = generic_rsrc.ResourceWithDefaultClientNameMultiExt
self.assertFalse(rsrc.is_service_available(
context=mock.Mock())[0])
mock_client_plugin_method.assert_called_once_with(
generic_rsrc.ResourceWithDefaultClientName.default_client_name)
mock_service_types.assert_called_once_with()
mock_client_plugin.does_endpoint_exist.assert_called_once_with(
service_type='test_type',
service_name=(generic_rsrc.ResourceWithDefaultClientName
.default_client_name))
mock_client_plugin.has_extension.has_calls(
[('foo'), ('bar')])
class TestLiveStateUpdate(common.HeatTestCase):

View File

@ -0,0 +1,6 @@
---
other:
- |
Allow Heat resources to accept more than one required_service_extension.
For cases where a resource required multiple service extensions. A
developer can now provide a list of those extensions.