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)
|
||||
|
||||
if not ResourceClass.is_service_available(stack.context):
|
||||
ex = exception.ResourceTypeUnavailable(
|
||||
service_name=ResourceClass.default_client_name,
|
||||
resource_type=definition.resource_type
|
||||
if not stack.service_check_defer:
|
||||
ResourceClass._validate_service_availability(
|
||||
stack.context,
|
||||
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))
|
||||
|
||||
raise ex
|
||||
|
||||
return super(Resource, cls).__new__(ResourceClass)
|
||||
|
||||
def _init_attributes(self):
|
||||
"""The method that defines attribute initialization for a resource.
|
||||
|
||||
@ -1138,6 +1146,12 @@ class Resource(object):
|
||||
in an overridden validate() such as accessing properties
|
||||
may not work.
|
||||
"""
|
||||
if self.stack.service_check_defer:
|
||||
self._validate_service_availability(
|
||||
self.stack.context,
|
||||
self.t.resource_type
|
||||
)
|
||||
|
||||
function.validate(self.t)
|
||||
self.validate_deletion_policy(self.t.deletion_policy())
|
||||
self.t.update_policy(self.update_policy_schema,
|
||||
|
@ -292,7 +292,7 @@ class EngineService(service.Service):
|
||||
by the RPC caller.
|
||||
"""
|
||||
|
||||
RPC_API_VERSION = '1.23'
|
||||
RPC_API_VERSION = '1.24'
|
||||
|
||||
def __init__(self, host, topic):
|
||||
super(EngineService, self).__init__()
|
||||
@ -995,7 +995,8 @@ class EngineService(service.Service):
|
||||
|
||||
@context.request_context
|
||||
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.
|
||||
|
||||
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
|
||||
:type environment_files: list or None
|
||||
: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'))
|
||||
if template is None:
|
||||
msg = _("No Template provided.")
|
||||
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)
|
||||
tmpl = templatem.Template(template, files=files, env=env)
|
||||
try:
|
||||
@ -1024,10 +1038,12 @@ class EngineService(service.Service):
|
||||
return {'Error': six.text_type(ex)}
|
||||
|
||||
stack_name = 'dummy'
|
||||
stack = parser.Stack(cnxt, stack_name, tmpl, strict_validate=False)
|
||||
stack.resource_validate = False
|
||||
stack = parser.Stack(cnxt, stack_name, tmpl,
|
||||
strict_validate=False,
|
||||
resource_validate=False,
|
||||
service_check_defer=service_check_defer)
|
||||
try:
|
||||
stack.validate()
|
||||
stack.validate(ignorable_errors=ignorable_errors)
|
||||
except exception.StackValidationFailed as ex:
|
||||
return {'Error': six.text_type(ex)}
|
||||
|
||||
|
@ -120,7 +120,8 @@ class Stack(collections.Mapping):
|
||||
use_stored_context=False, username=None,
|
||||
nested_depth=0, strict_validate=True, convergence=False,
|
||||
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.
|
||||
|
||||
@ -193,6 +194,12 @@ class Stack(collections.Mapping):
|
||||
# commonly done in plugin validate() methods
|
||||
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:
|
||||
self.context = self.stored_context()
|
||||
self.context.roles = self.context.clients.client(
|
||||
@ -671,7 +678,7 @@ class Stack(collections.Mapping):
|
||||
return handler and handler(resource_name)
|
||||
|
||||
@profiler.trace('Stack.validate', hide_args=False)
|
||||
def validate(self):
|
||||
def validate(self, ignorable_errors=None):
|
||||
"""Validates the stack."""
|
||||
# TODO(sdake) Should return line number of invalid reference
|
||||
|
||||
@ -706,7 +713,10 @@ class Stack(collections.Mapping):
|
||||
result = res.validate_template()
|
||||
except exception.HeatException as ex:
|
||||
LOG.debug('%s', ex)
|
||||
raise
|
||||
if ignorable_errors and ex.error_code in ignorable_errors:
|
||||
result = None
|
||||
else:
|
||||
raise ex
|
||||
except AssertionError:
|
||||
raise
|
||||
except Exception as ex:
|
||||
|
@ -40,7 +40,7 @@ class ServiceEngineTest(common.HeatTestCase):
|
||||
|
||||
def test_make_sure_rpc_version(self):
|
||||
self.assertEqual(
|
||||
'1.23',
|
||||
'1.24',
|
||||
service.EngineService.RPC_API_VERSION,
|
||||
('RPC version is changed, please update this test to new version '
|
||||
'and make sure additional test cases are added for RPC APIs '
|
||||
|
@ -3128,6 +3128,7 @@ class ResourceAvailabilityTest(common.HeatTestCase):
|
||||
resource_type='UnavailableResourceType')
|
||||
|
||||
mock_stack = mock.MagicMock()
|
||||
mock_stack.service_check_defer = False
|
||||
|
||||
ex = self.assertRaises(
|
||||
exception.ResourceTypeUnavailable,
|
||||
|
@ -14,6 +14,7 @@
|
||||
import mock
|
||||
from oslo_messaging.rpc import dispatcher
|
||||
import six
|
||||
import webob
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common.i18n import _
|
||||
@ -1668,3 +1669,38 @@ class ValidateTest(common.HeatTestCase):
|
||||
t,
|
||||
{})
|
||||
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…
Reference in New Issue
Block a user