Return nested parameters for resource group.
This refactors the building of schema from parameter validation to use a new method (which doesn't keep stacks in memory), and use that new method for providing proper schema for resource group when the size is 0. Change-Id: Id3020e8f3fd94e2cef413d5eb9de9d1cd16ddeaa Closes-Bug: #1751074 Closes-Bug: #1626025
This commit is contained in:
parent
7f96c2198a
commit
9f73a232d7
|
@ -576,6 +576,13 @@ class Resource(status.ResourceStatus):
|
||||||
"""
|
"""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_nested_parameters_stack(self):
|
||||||
|
"""Return the nested stack for schema validation.
|
||||||
|
|
||||||
|
Regular resources don't have such a thing.
|
||||||
|
"""
|
||||||
|
return
|
||||||
|
|
||||||
def has_hook(self, hook):
|
def has_hook(self, hook):
|
||||||
# Clear the cache to make sure the data is up to date:
|
# Clear the cache to make sure the data is up to date:
|
||||||
self._data = None
|
self._data = None
|
||||||
|
|
|
@ -454,6 +454,13 @@ class InstanceGroup(stack_resource.StackResource):
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_nested_parameters_stack(self):
|
||||||
|
"""Return a nested group of size 1 for validation."""
|
||||||
|
child_template = self._create_template(1)
|
||||||
|
params = self.child_params()
|
||||||
|
name = "%s-%s" % (self.stack.name, self.name)
|
||||||
|
return self._parse_nested_stack(name, child_template, params)
|
||||||
|
|
||||||
|
|
||||||
def resource_mapping():
|
def resource_mapping():
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -790,6 +790,14 @@ class ResourceGroup(stack_resource.StackResource):
|
||||||
{},
|
{},
|
||||||
adopt_data=resource_data)
|
adopt_data=resource_data)
|
||||||
|
|
||||||
|
def get_nested_parameters_stack(self):
|
||||||
|
"""Return a nested group of size 1 for validation."""
|
||||||
|
names = self._resource_names(1)
|
||||||
|
child_template = self._assemble_nested(names)
|
||||||
|
params = self.child_params()
|
||||||
|
name = "%s-%s" % (self.stack.name, self.name)
|
||||||
|
return self._parse_nested_stack(name, child_template, params)
|
||||||
|
|
||||||
|
|
||||||
def resource_mapping():
|
def resource_mapping():
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -199,6 +199,24 @@ class StackResource(resource.Resource):
|
||||||
|
|
||||||
return self.nested().preview_resources()
|
return self.nested().preview_resources()
|
||||||
|
|
||||||
|
def get_nested_parameters_stack(self):
|
||||||
|
"""Return a stack for schema validation.
|
||||||
|
|
||||||
|
This returns a stack to be introspected for building parameters schema.
|
||||||
|
It can be customized by subclass to return a restricted version of what
|
||||||
|
will be running.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
child_template = self.child_template()
|
||||||
|
params = self.child_params()
|
||||||
|
except NotImplementedError:
|
||||||
|
class_name = reflection.get_class_name(self, fully_qualified=False)
|
||||||
|
LOG.warning("Nested parameters of '%s' not yet "
|
||||||
|
"implemented", class_name)
|
||||||
|
return
|
||||||
|
name = "%s-%s" % (self.stack.name, self.name)
|
||||||
|
return self._parse_nested_stack(name, child_template, params)
|
||||||
|
|
||||||
def _parse_child_template(self, child_template, child_env):
|
def _parse_child_template(self, child_template, child_env):
|
||||||
parsed_child_template = child_template
|
parsed_child_template = child_template
|
||||||
if isinstance(parsed_child_template, template.Template):
|
if isinstance(parsed_child_template, template.Template):
|
||||||
|
|
|
@ -1238,36 +1238,7 @@ class EngineService(service.ServiceBase):
|
||||||
result['ParameterGroups'] = param_groups.parameter_groups
|
result['ParameterGroups'] = param_groups.parameter_groups
|
||||||
|
|
||||||
if show_nested:
|
if show_nested:
|
||||||
# Note preview_resources is needed here to build the tree
|
result.update(stack.get_nested_parameters(filter_parameter))
|
||||||
# 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
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add parameter_groups if it is present in nested stack
|
|
||||||
nested_pg = parameter_groups.ParameterGroups(
|
|
||||||
stk[r].nested().t)
|
|
||||||
if nested_pg.parameter_groups:
|
|
||||||
n_result[r].update({'ParameterGroups':
|
|
||||||
nested_pg.parameter_groups})
|
|
||||||
|
|
||||||
n_result[r].update(nested_params(stk[r].nested()))
|
|
||||||
return {'NestedParameters': n_result} if n_result else {}
|
|
||||||
|
|
||||||
result.update(nested_params(stack))
|
|
||||||
|
|
||||||
result['Environment'] = tmpl.env.user_env_as_dict()
|
result['Environment'] = tmpl.env.user_env_as_dict()
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -32,6 +32,7 @@ from heat.common import exception
|
||||||
from heat.common.i18n import _
|
from heat.common.i18n import _
|
||||||
from heat.common import identifier
|
from heat.common import identifier
|
||||||
from heat.common import lifecycle_plugin_utils
|
from heat.common import lifecycle_plugin_utils
|
||||||
|
from heat.engine import api
|
||||||
from heat.engine import dependencies
|
from heat.engine import dependencies
|
||||||
from heat.engine import environment
|
from heat.engine import environment
|
||||||
from heat.engine import event
|
from heat.engine import event
|
||||||
|
@ -1051,6 +1052,36 @@ class Stack(collections.Mapping):
|
||||||
return [resource.preview()
|
return [resource.preview()
|
||||||
for resource in six.itervalues(self.resources)]
|
for resource in six.itervalues(self.resources)]
|
||||||
|
|
||||||
|
def get_nested_parameters(self, filter_func):
|
||||||
|
"""Return nested parameters schema, if any.
|
||||||
|
|
||||||
|
This introspects the resources to return the parameters of the nested
|
||||||
|
stacks. It uses the `get_nested_parameters_stack` API to build the
|
||||||
|
stack.
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
for name, rsrc in six.iteritems(self.resources):
|
||||||
|
nested = rsrc.get_nested_parameters_stack()
|
||||||
|
if nested is None:
|
||||||
|
continue
|
||||||
|
nested_params = nested.parameters.map(
|
||||||
|
api.format_validate_parameter,
|
||||||
|
filter_func=filter_func)
|
||||||
|
params = {
|
||||||
|
'Type': rsrc.type(),
|
||||||
|
'Description': nested.t.get('Description', ''),
|
||||||
|
'Parameters': nested_params
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add parameter_groups if it is present in nested stack
|
||||||
|
nested_pg = param_groups.ParameterGroups(nested.t)
|
||||||
|
if nested_pg.parameter_groups:
|
||||||
|
params.update({'ParameterGroups': nested_pg.parameter_groups})
|
||||||
|
|
||||||
|
params.update(nested.get_nested_parameters(filter_func))
|
||||||
|
result[name] = params
|
||||||
|
return {'NestedParameters': result} if result else {}
|
||||||
|
|
||||||
def _store_resources(self):
|
def _store_resources(self):
|
||||||
for r in reversed(self.dependencies):
|
for r in reversed(self.dependencies):
|
||||||
if r.action == r.INIT:
|
if r.action == r.INIT:
|
||||||
|
|
|
@ -2039,3 +2039,69 @@ parameter_groups:
|
||||||
self.ctx, t, {})
|
self.ctx, t, {})
|
||||||
self.assertEqual(exception.InvalidSchemaError,
|
self.assertEqual(exception.InvalidSchemaError,
|
||||||
exc.exc_info[0])
|
exc.exc_info[0])
|
||||||
|
|
||||||
|
def test_validate_empty_resource_group(self):
|
||||||
|
engine = service.EngineService('a', 't')
|
||||||
|
params = {
|
||||||
|
"resource_registry": {
|
||||||
|
"OS::Test::TestResource": "https://server.test/nested.template"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root_template_str = '''
|
||||||
|
heat_template_version: 2015-10-15
|
||||||
|
parameters:
|
||||||
|
test_root_param:
|
||||||
|
type: string
|
||||||
|
resources:
|
||||||
|
Group:
|
||||||
|
type: OS::Heat::ResourceGroup
|
||||||
|
properties:
|
||||||
|
count: 0
|
||||||
|
resource_def:
|
||||||
|
type: OS::Test::TestResource
|
||||||
|
'''
|
||||||
|
nested_template_str = '''
|
||||||
|
heat_template_version: 2015-10-15
|
||||||
|
parameters:
|
||||||
|
test_param:
|
||||||
|
type: string
|
||||||
|
'''
|
||||||
|
root_template = template_format.parse(root_template_str)
|
||||||
|
|
||||||
|
self.patchobject(urlfetch, 'get')
|
||||||
|
urlfetch.get.return_value = nested_template_str
|
||||||
|
|
||||||
|
res = dict(engine.validate_template(self.ctx, root_template,
|
||||||
|
params, show_nested=True))
|
||||||
|
expected = {
|
||||||
|
'Description': 'No description',
|
||||||
|
'Environment': {
|
||||||
|
'event_sinks': [],
|
||||||
|
'parameter_defaults': {},
|
||||||
|
'parameters': {},
|
||||||
|
'resource_registry': {
|
||||||
|
'OS::Test::TestResource':
|
||||||
|
'https://server.test/nested.template',
|
||||||
|
'resources': {}}},
|
||||||
|
'NestedParameters': {
|
||||||
|
'Group': {
|
||||||
|
'Description': 'No description',
|
||||||
|
'Parameters': {},
|
||||||
|
'Type': 'OS::Heat::ResourceGroup',
|
||||||
|
'NestedParameters': {
|
||||||
|
'0': {
|
||||||
|
'Description': 'No description',
|
||||||
|
'Parameters': {
|
||||||
|
'test_param': {
|
||||||
|
'Description': '',
|
||||||
|
'Label': 'test_param',
|
||||||
|
'NoEcho': 'false',
|
||||||
|
'Type': 'String'}},
|
||||||
|
'Type': 'OS::Test::TestResource'}}}},
|
||||||
|
'Parameters': {
|
||||||
|
'test_root_param': {
|
||||||
|
'Description': '',
|
||||||
|
'Label': 'test_root_param',
|
||||||
|
'NoEcho': 'false',
|
||||||
|
'Type': 'String'}}}
|
||||||
|
self.assertEqual(expected, res)
|
||||||
|
|
|
@ -322,6 +322,37 @@ outputs:
|
||||||
updated_rand = self._stack_output(stack1, 'random1')
|
updated_rand = self._stack_output(stack1, 'random1')
|
||||||
self.assertNotEqual(initial_rand, updated_rand)
|
self.assertNotEqual(initial_rand, updated_rand)
|
||||||
|
|
||||||
|
def test_validation(self):
|
||||||
|
resource_group = '''
|
||||||
|
heat_template_version: 2016-10-14
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
the_count:
|
||||||
|
type: number
|
||||||
|
|
||||||
|
resources:
|
||||||
|
|
||||||
|
the_group:
|
||||||
|
type: OS::Heat::ResourceGroup
|
||||||
|
properties:
|
||||||
|
count: {get_param: the_count}
|
||||||
|
resource_def:
|
||||||
|
type: OS::Heat::RandomString
|
||||||
|
'''
|
||||||
|
ret = self.client.stacks.validate(template=resource_group)
|
||||||
|
expected = {'Description': 'No description',
|
||||||
|
'Environment': {'event_sinks': [],
|
||||||
|
'parameter_defaults': {},
|
||||||
|
'parameters': {},
|
||||||
|
'resource_registry': {u'resources': {}}},
|
||||||
|
'Parameters': {
|
||||||
|
'the_count': {'Description': '',
|
||||||
|
'Label': 'the_count',
|
||||||
|
'NoEcho': 'false',
|
||||||
|
'Type': 'Number'}}}
|
||||||
|
|
||||||
|
self.assertEqual(expected, ret)
|
||||||
|
|
||||||
|
|
||||||
class ResourceGroupTestNullParams(functional_base.FunctionalTestsBase):
|
class ResourceGroupTestNullParams(functional_base.FunctionalTestsBase):
|
||||||
template = '''
|
template = '''
|
||||||
|
|
Loading…
Reference in New Issue