Allow nested validation for template-validate
Adds an optional flag that causes template-validate to recursively validate nested stacks and expose a schema of the parameters for all stacks in the tree. This uses a similar method to stack preview in that it builds the tree in memory so we can then walk it and extract the parameter data we need. Change-Id: I7e3d752124997a0a7b8ff53e774ed8034dd2f81d Co-Authored-By: Jay Dobies <jdobies@redhat.com> Implements-blueprint: nested-validation
This commit is contained in:
parent
a7ec465031
commit
506919fcf6
@ -535,10 +535,20 @@ class StackController(object):
|
||||
|
||||
data = InstantiationData(body)
|
||||
|
||||
whitelist = {'show_nested': 'single'}
|
||||
params = util.get_allowed_params(req.params, whitelist)
|
||||
|
||||
show_nested = False
|
||||
p_name = rpc_api.PARAM_SHOW_NESTED
|
||||
if p_name in params:
|
||||
params[p_name] = self._extract_bool_param(p_name, params[p_name])
|
||||
show_nested = params[p_name]
|
||||
|
||||
result = self.rpc_client.validate_template(req.context,
|
||||
data.template(),
|
||||
data.environment(),
|
||||
files=data.files())
|
||||
files=data.files(),
|
||||
show_nested=show_nested)
|
||||
|
||||
if 'Error' in result:
|
||||
raise exc.HTTPBadRequest(result['Error'])
|
||||
|
@ -271,7 +271,7 @@ class EngineService(service.Service):
|
||||
by the RPC caller.
|
||||
"""
|
||||
|
||||
RPC_API_VERSION = '1.17'
|
||||
RPC_API_VERSION = '1.18'
|
||||
|
||||
def __init__(self, host, topic):
|
||||
super(EngineService, self).__init__()
|
||||
@ -953,13 +953,17 @@ class EngineService(service.Service):
|
||||
engine_id=engine_id)
|
||||
|
||||
@context.request_context
|
||||
def validate_template(self, cnxt, template, params=None, files=None):
|
||||
"""Uses the stack parser to check the validity of a template.
|
||||
def validate_template(self, cnxt, template, params=None, files=None,
|
||||
show_nested=False):
|
||||
"""
|
||||
The validate_template method uses the stack parser to check
|
||||
the validity of a template.
|
||||
|
||||
:param cnxt: RPC context.
|
||||
:param template: Template of stack you want to create.
|
||||
:param params: Stack Input Params
|
||||
:param files: Files referenced from the template
|
||||
:param show_nested: if True, any nested templates will be checked
|
||||
"""
|
||||
LOG.info(_LI('validate_template'))
|
||||
if template is None:
|
||||
@ -996,6 +1000,30 @@ class EngineService(service.Service):
|
||||
if param_groups.parameter_groups:
|
||||
result['ParameterGroups'] = param_groups.parameter_groups
|
||||
|
||||
if show_nested:
|
||||
# Note preview_resources is needed here to build the tree
|
||||
# of nested resources/stacks in memory, otherwise the
|
||||
# nested/has_nested() tests below won't work
|
||||
stack.preview_resources()
|
||||
|
||||
def nested_params(stk):
|
||||
n_result = {}
|
||||
for r in stk:
|
||||
if stk[r].has_nested():
|
||||
n_params = stk[r].nested().parameters.map(
|
||||
api.format_validate_parameter,
|
||||
filter_func=filter_parameter)
|
||||
n_result[r] = {
|
||||
'Type': stk[r].type(),
|
||||
'Description': stk[r].nested().t.get(
|
||||
'Description', ''),
|
||||
'Parameters': n_params
|
||||
}
|
||||
n_result[r].update(nested_params(stk[r].nested()))
|
||||
return {'NestedParameters': n_result} if n_result else {}
|
||||
|
||||
result.update(nested_params(stack))
|
||||
|
||||
return result
|
||||
|
||||
@context.request_context
|
||||
|
@ -38,6 +38,7 @@ class EngineClient(object):
|
||||
1.15 - Add preview_update_stack() call
|
||||
1.16 - Adds version, type_name to list_resource_types()
|
||||
1.17 - Add files to validate_template
|
||||
1.18 - Add show_nested to validate_template
|
||||
'''
|
||||
|
||||
BASE_RPC_API_VERSION = '1.0'
|
||||
@ -293,7 +294,8 @@ class EngineClient(object):
|
||||
),
|
||||
version='1.15')
|
||||
|
||||
def validate_template(self, ctxt, template, params=None, files=None):
|
||||
def validate_template(self, ctxt, template, params=None, files=None,
|
||||
show_nested=False):
|
||||
"""
|
||||
The validate_template method uses the stack parser to check
|
||||
the validity of a template.
|
||||
@ -302,12 +304,14 @@ class EngineClient(object):
|
||||
:param template: Template of stack you want to create.
|
||||
:param params: Stack Input Params/Environment
|
||||
:param files: files referenced from the environment/template.
|
||||
:param show_nested: if True nested templates will be validated
|
||||
"""
|
||||
return self.call(ctxt, self.make_msg('validate_template',
|
||||
template=template,
|
||||
params=params,
|
||||
files=files),
|
||||
version='1.17')
|
||||
files=files,
|
||||
show_nested=show_nested),
|
||||
version='1.18')
|
||||
|
||||
def authenticated_to_backend(self, ctxt):
|
||||
"""
|
||||
|
@ -1074,8 +1074,8 @@ class CfnStackControllerTest(common.HeatTestCase):
|
||||
rpc_client.EngineClient.call(
|
||||
dummy_req.context,
|
||||
('validate_template', {'template': json_template, 'params': None,
|
||||
'files': None}),
|
||||
version='1.17'
|
||||
'files': None, 'show_nested': False}),
|
||||
version='1.18'
|
||||
).AndReturn(response)
|
||||
self.m.ReplayAll()
|
||||
|
||||
|
@ -2059,8 +2059,9 @@ class StackControllerTest(tools.ControllerTest, common.HeatTestCase):
|
||||
'encrypted_param_names': [],
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}},
|
||||
'files': {}}),
|
||||
version='1.17'
|
||||
'files': {},
|
||||
'show_nested': False}),
|
||||
version='1.18'
|
||||
).AndReturn(engine_response)
|
||||
self.m.ReplayAll()
|
||||
|
||||
@ -2086,8 +2087,9 @@ class StackControllerTest(tools.ControllerTest, common.HeatTestCase):
|
||||
'encrypted_param_names': [],
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}},
|
||||
'files': {}}),
|
||||
version='1.17'
|
||||
'files': {},
|
||||
'show_nested': False}),
|
||||
version='1.18'
|
||||
).AndReturn({'Error': 'fubar'})
|
||||
self.m.ReplayAll()
|
||||
|
||||
|
@ -39,7 +39,7 @@ class ServiceEngineTest(common.HeatTestCase):
|
||||
|
||||
def test_make_sure_rpc_version(self):
|
||||
self.assertEqual(
|
||||
'1.17',
|
||||
'1.18',
|
||||
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 '
|
||||
|
@ -199,7 +199,8 @@ class EngineRpcAPITestCase(common.HeatTestCase):
|
||||
self._test_engine_api('validate_template', 'call',
|
||||
template={u'Foo': u'bar'},
|
||||
params={u'Egg': u'spam'},
|
||||
files=None)
|
||||
files=None,
|
||||
show_nested=False)
|
||||
|
||||
def test_list_resource_types(self):
|
||||
self._test_engine_api('list_resource_types',
|
||||
|
@ -35,6 +35,29 @@ resources:
|
||||
length: {get_param: aparam}
|
||||
'''
|
||||
|
||||
parent_template = '''
|
||||
heat_template_version: 2014-10-16
|
||||
description: the parent template
|
||||
parameters:
|
||||
pparam:
|
||||
type: number
|
||||
default: 5
|
||||
description: the param description
|
||||
resources:
|
||||
nres:
|
||||
type: mynested.yaml
|
||||
properties:
|
||||
aparam: {get_param: pparam}
|
||||
'''
|
||||
|
||||
parent_template_noprop = '''
|
||||
heat_template_version: 2014-10-16
|
||||
description: the parent template
|
||||
resources:
|
||||
nres:
|
||||
type: mynested.yaml
|
||||
'''
|
||||
|
||||
random_template_groups = '''
|
||||
heat_template_version: 2014-10-16
|
||||
description: the stack description
|
||||
@ -147,3 +170,75 @@ resources:
|
||||
'NoEcho': 'true',
|
||||
'Type': 'String'}}}
|
||||
self.assertEqual(expected, ret)
|
||||
|
||||
def test_template_validate_nested_off(self):
|
||||
files = {'mynested.yaml': self.random_template}
|
||||
ret = self.client.stacks.validate(template=self.parent_template,
|
||||
files=files)
|
||||
expected = {'Description': 'the parent template',
|
||||
'Parameters': {
|
||||
'pparam': {'Default': 5,
|
||||
'Description': 'the param description',
|
||||
'Label': 'pparam',
|
||||
'NoEcho': 'false',
|
||||
'Type': 'Number'}}}
|
||||
self.assertEqual(expected, ret)
|
||||
|
||||
def test_template_validate_nested_on(self):
|
||||
files = {'mynested.yaml': self.random_template}
|
||||
ret = self.client.stacks.validate(template=self.parent_template_noprop,
|
||||
files=files,
|
||||
show_nested=True)
|
||||
expected = {'Description': 'the parent template',
|
||||
'Parameters': {},
|
||||
'NestedParameters': {
|
||||
'nres': {'Description': 'the stack description',
|
||||
'Parameters': {'aparam': {'Default': 10,
|
||||
'Description':
|
||||
'the param '
|
||||
'description',
|
||||
'Label': 'aparam',
|
||||
'NoEcho': 'false',
|
||||
'Type': 'Number'}},
|
||||
'Type': 'mynested.yaml'}}}
|
||||
self.assertEqual(expected, ret)
|
||||
|
||||
def test_template_validate_nested_on_multiple(self):
|
||||
# parent_template -> nested_template -> random_template
|
||||
nested_template = self.random_template.replace(
|
||||
'OS::Heat::RandomString', 'mynested2.yaml')
|
||||
files = {'mynested.yaml': nested_template,
|
||||
'mynested2.yaml': self.random_template}
|
||||
ret = self.client.stacks.validate(template=self.parent_template,
|
||||
files=files,
|
||||
show_nested=True)
|
||||
|
||||
n_param2 = {'myres': {'Description': 'the stack description',
|
||||
'Parameters': {'aparam': {'Default': 10,
|
||||
'Description':
|
||||
'the param '
|
||||
'description',
|
||||
'Label': 'aparam',
|
||||
'NoEcho': 'false',
|
||||
'Type': 'Number'}},
|
||||
'Type': 'mynested2.yaml'}}
|
||||
expected = {'Description': 'the parent template',
|
||||
'Parameters': {
|
||||
'pparam': {'Default': 5,
|
||||
'Description': 'the param description',
|
||||
'Label': 'pparam',
|
||||
'NoEcho': 'false',
|
||||
'Type': 'Number'}},
|
||||
'NestedParameters': {
|
||||
'nres': {'Description': 'the stack description',
|
||||
'Parameters': {'aparam': {'Default': 10,
|
||||
'Description':
|
||||
'the param '
|
||||
'description',
|
||||
'Label': 'aparam',
|
||||
'Value': 5,
|
||||
'NoEcho': 'false',
|
||||
'Type': 'Number'}},
|
||||
'NestedParameters': n_param2,
|
||||
'Type': 'mynested.yaml'}}}
|
||||
self.assertEqual(expected, ret)
|
||||
|
Loading…
Reference in New Issue
Block a user