Merge "Adds ignore_errors for template_validate in engine"
This commit is contained in:
commit
dd752fbac6
@ -140,17 +140,25 @@ class Resource(object):
|
|||||||
|
|
||||||
assert issubclass(ResourceClass, Resource)
|
assert issubclass(ResourceClass, Resource)
|
||||||
|
|
||||||
if not ResourceClass.is_service_available(stack.context):
|
if not stack.service_check_defer:
|
||||||
ex = exception.ResourceTypeUnavailable(
|
ResourceClass._validate_service_availability(
|
||||||
service_name=ResourceClass.default_client_name,
|
stack.context,
|
||||||
resource_type=definition.resource_type
|
definition.resource_type
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return super(Resource, cls).__new__(ResourceClass)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _validate_service_availability(cls, context, resource_type):
|
||||||
|
if not cls.is_service_available(context):
|
||||||
|
ex = exception.ResourceTypeUnavailable(
|
||||||
|
service_name=cls.default_client_name,
|
||||||
|
resource_type=resource_type
|
||||||
|
)
|
||||||
LOG.info(six.text_type(ex))
|
LOG.info(six.text_type(ex))
|
||||||
|
|
||||||
raise ex
|
raise ex
|
||||||
|
|
||||||
return super(Resource, cls).__new__(ResourceClass)
|
|
||||||
|
|
||||||
def _init_attributes(self):
|
def _init_attributes(self):
|
||||||
"""The method that defines attribute initialization for a resource.
|
"""The method that defines attribute initialization for a resource.
|
||||||
|
|
||||||
@ -1138,6 +1146,12 @@ class Resource(object):
|
|||||||
in an overridden validate() such as accessing properties
|
in an overridden validate() such as accessing properties
|
||||||
may not work.
|
may not work.
|
||||||
"""
|
"""
|
||||||
|
if self.stack.service_check_defer:
|
||||||
|
self._validate_service_availability(
|
||||||
|
self.stack.context,
|
||||||
|
self.t.resource_type
|
||||||
|
)
|
||||||
|
|
||||||
function.validate(self.t)
|
function.validate(self.t)
|
||||||
self.validate_deletion_policy(self.t.deletion_policy())
|
self.validate_deletion_policy(self.t.deletion_policy())
|
||||||
self.t.update_policy(self.update_policy_schema,
|
self.t.update_policy(self.update_policy_schema,
|
||||||
|
@ -292,7 +292,7 @@ class EngineService(service.Service):
|
|||||||
by the RPC caller.
|
by the RPC caller.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
RPC_API_VERSION = '1.23'
|
RPC_API_VERSION = '1.24'
|
||||||
|
|
||||||
def __init__(self, host, topic):
|
def __init__(self, host, topic):
|
||||||
super(EngineService, self).__init__()
|
super(EngineService, self).__init__()
|
||||||
@ -995,7 +995,8 @@ class EngineService(service.Service):
|
|||||||
|
|
||||||
@context.request_context
|
@context.request_context
|
||||||
def validate_template(self, cnxt, template, params=None, files=None,
|
def validate_template(self, cnxt, template, params=None, files=None,
|
||||||
environment_files=None, show_nested=False):
|
environment_files=None, show_nested=False,
|
||||||
|
ignorable_errors=None):
|
||||||
"""Check the validity of a template.
|
"""Check the validity of a template.
|
||||||
|
|
||||||
Checks, so far as we can, that a template is valid, and returns
|
Checks, so far as we can, that a template is valid, and returns
|
||||||
@ -1010,12 +1011,25 @@ class EngineService(service.Service):
|
|||||||
names included in the files dict
|
names included in the files dict
|
||||||
:type environment_files: list or None
|
:type environment_files: list or None
|
||||||
:param show_nested: if True, any nested templates will be checked
|
:param show_nested: if True, any nested templates will be checked
|
||||||
|
:param ignorable_errors: List of error_code to be ignored as part of
|
||||||
|
validation
|
||||||
"""
|
"""
|
||||||
LOG.info(_LI('validate_template'))
|
LOG.info(_LI('validate_template'))
|
||||||
if template is None:
|
if template is None:
|
||||||
msg = _("No Template provided.")
|
msg = _("No Template provided.")
|
||||||
return webob.exc.HTTPBadRequest(explanation=msg)
|
return webob.exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
|
service_check_defer = False
|
||||||
|
if ignorable_errors:
|
||||||
|
invalid_codes = (set(ignorable_errors) -
|
||||||
|
set(exception.ERROR_CODE_MAP.keys()))
|
||||||
|
if invalid_codes:
|
||||||
|
msg = (_("Invalid codes in ignore_errors : %s") %
|
||||||
|
list(invalid_codes))
|
||||||
|
return webob.exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
|
service_check_defer = True
|
||||||
|
|
||||||
env = environment.Environment(params)
|
env = environment.Environment(params)
|
||||||
tmpl = templatem.Template(template, files=files, env=env)
|
tmpl = templatem.Template(template, files=files, env=env)
|
||||||
try:
|
try:
|
||||||
@ -1024,10 +1038,12 @@ class EngineService(service.Service):
|
|||||||
return {'Error': six.text_type(ex)}
|
return {'Error': six.text_type(ex)}
|
||||||
|
|
||||||
stack_name = 'dummy'
|
stack_name = 'dummy'
|
||||||
stack = parser.Stack(cnxt, stack_name, tmpl, strict_validate=False)
|
stack = parser.Stack(cnxt, stack_name, tmpl,
|
||||||
stack.resource_validate = False
|
strict_validate=False,
|
||||||
|
resource_validate=False,
|
||||||
|
service_check_defer=service_check_defer)
|
||||||
try:
|
try:
|
||||||
stack.validate()
|
stack.validate(ignorable_errors=ignorable_errors)
|
||||||
except exception.StackValidationFailed as ex:
|
except exception.StackValidationFailed as ex:
|
||||||
return {'Error': six.text_type(ex)}
|
return {'Error': six.text_type(ex)}
|
||||||
|
|
||||||
|
@ -120,7 +120,8 @@ class Stack(collections.Mapping):
|
|||||||
use_stored_context=False, username=None,
|
use_stored_context=False, username=None,
|
||||||
nested_depth=0, strict_validate=True, convergence=False,
|
nested_depth=0, strict_validate=True, convergence=False,
|
||||||
current_traversal=None, tags=None, prev_raw_template_id=None,
|
current_traversal=None, tags=None, prev_raw_template_id=None,
|
||||||
current_deps=None, cache_data=None, resource_validate=True):
|
current_deps=None, cache_data=None, resource_validate=True,
|
||||||
|
service_check_defer=False):
|
||||||
|
|
||||||
"""Initialise the Stack.
|
"""Initialise the Stack.
|
||||||
|
|
||||||
@ -193,6 +194,12 @@ class Stack(collections.Mapping):
|
|||||||
# commonly done in plugin validate() methods
|
# commonly done in plugin validate() methods
|
||||||
self.resource_validate = resource_validate
|
self.resource_validate = resource_validate
|
||||||
|
|
||||||
|
# service_check_defer can be used to defer the validation of service
|
||||||
|
# availability for a given resource, which helps to create the resource
|
||||||
|
# dependency tree completely when respective service is not available,
|
||||||
|
# especially during template_validate
|
||||||
|
self.service_check_defer = service_check_defer
|
||||||
|
|
||||||
if use_stored_context:
|
if use_stored_context:
|
||||||
self.context = self.stored_context()
|
self.context = self.stored_context()
|
||||||
self.context.roles = self.context.clients.client(
|
self.context.roles = self.context.clients.client(
|
||||||
@ -671,7 +678,7 @@ class Stack(collections.Mapping):
|
|||||||
return handler and handler(resource_name)
|
return handler and handler(resource_name)
|
||||||
|
|
||||||
@profiler.trace('Stack.validate', hide_args=False)
|
@profiler.trace('Stack.validate', hide_args=False)
|
||||||
def validate(self):
|
def validate(self, ignorable_errors=None):
|
||||||
"""Validates the stack."""
|
"""Validates the stack."""
|
||||||
# TODO(sdake) Should return line number of invalid reference
|
# TODO(sdake) Should return line number of invalid reference
|
||||||
|
|
||||||
@ -706,7 +713,10 @@ class Stack(collections.Mapping):
|
|||||||
result = res.validate_template()
|
result = res.validate_template()
|
||||||
except exception.HeatException as ex:
|
except exception.HeatException as ex:
|
||||||
LOG.debug('%s', ex)
|
LOG.debug('%s', ex)
|
||||||
raise
|
if ignorable_errors and ex.error_code in ignorable_errors:
|
||||||
|
result = None
|
||||||
|
else:
|
||||||
|
raise ex
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
raise
|
raise
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
|
@ -40,7 +40,7 @@ class ServiceEngineTest(common.HeatTestCase):
|
|||||||
|
|
||||||
def test_make_sure_rpc_version(self):
|
def test_make_sure_rpc_version(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'1.23',
|
'1.24',
|
||||||
service.EngineService.RPC_API_VERSION,
|
service.EngineService.RPC_API_VERSION,
|
||||||
('RPC version is changed, please update this test to new version '
|
('RPC version is changed, please update this test to new version '
|
||||||
'and make sure additional test cases are added for RPC APIs '
|
'and make sure additional test cases are added for RPC APIs '
|
||||||
|
@ -3128,6 +3128,7 @@ class ResourceAvailabilityTest(common.HeatTestCase):
|
|||||||
resource_type='UnavailableResourceType')
|
resource_type='UnavailableResourceType')
|
||||||
|
|
||||||
mock_stack = mock.MagicMock()
|
mock_stack = mock.MagicMock()
|
||||||
|
mock_stack.service_check_defer = False
|
||||||
|
|
||||||
ex = self.assertRaises(
|
ex = self.assertRaises(
|
||||||
exception.ResourceTypeUnavailable,
|
exception.ResourceTypeUnavailable,
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
import mock
|
import mock
|
||||||
from oslo_messaging.rpc import dispatcher
|
from oslo_messaging.rpc import dispatcher
|
||||||
import six
|
import six
|
||||||
|
import webob
|
||||||
|
|
||||||
from heat.common import exception
|
from heat.common import exception
|
||||||
from heat.common.i18n import _
|
from heat.common.i18n import _
|
||||||
@ -1668,3 +1669,38 @@ class ValidateTest(common.HeatTestCase):
|
|||||||
t,
|
t,
|
||||||
{})
|
{})
|
||||||
self.assertEqual(exception.ResourceTypeUnavailable, ex.exc_info[0])
|
self.assertEqual(exception.ResourceTypeUnavailable, ex.exc_info[0])
|
||||||
|
|
||||||
|
def test_validate_with_ignorable_errors(self):
|
||||||
|
t = template_format.parse(
|
||||||
|
"""
|
||||||
|
heat_template_version: 2015-10-15
|
||||||
|
resources:
|
||||||
|
my_instance:
|
||||||
|
type: AWS::EC2::Instance
|
||||||
|
""")
|
||||||
|
engine = service.EngineService('a', 't')
|
||||||
|
self.mock_is_service_available.return_value = False
|
||||||
|
|
||||||
|
res = dict(engine.validate_template(
|
||||||
|
self.ctx,
|
||||||
|
t,
|
||||||
|
{},
|
||||||
|
ignorable_errors=[exception.ResourceTypeUnavailable.error_code]))
|
||||||
|
expected = {'Description': 'No description', 'Parameters': {}}
|
||||||
|
self.assertEqual(expected, res)
|
||||||
|
|
||||||
|
def test_validate_with_ignorable_errors_invalid_error_code(self):
|
||||||
|
engine = service.EngineService('a', 't')
|
||||||
|
|
||||||
|
invalide_error_code = '123456'
|
||||||
|
invalid_codes = ['99001', invalide_error_code]
|
||||||
|
res = engine.validate_template(
|
||||||
|
self.ctx,
|
||||||
|
mock.MagicMock(),
|
||||||
|
{},
|
||||||
|
ignorable_errors=invalid_codes)
|
||||||
|
|
||||||
|
msg = _("Invalid codes in ignore_errors : %s") % [invalide_error_code]
|
||||||
|
ex = webob.exc.HTTPBadRequest(explanation=msg)
|
||||||
|
self.assertIsInstance(res, webob.exc.HTTPBadRequest)
|
||||||
|
self.assertEqual(ex.explanation, res.explanation)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user