Add "parameter_defaults" to the environment
This give the user a way to set defaults recursively down nested stacks without having to create the parameter in every template (it's ignored if the template does not have the parameter). blueprint env-nested-usability Change-Id: Ie6b4481417204a527d322fd532c341b9acbce473
This commit is contained in:
parent
e1540f834c
commit
08431c7c06
@ -29,8 +29,8 @@ what plug-ins the cloud operator has installed.
|
||||
Format
|
||||
------
|
||||
|
||||
It is a yaml text file with two main sections "resource_registry" and
|
||||
"parameters".
|
||||
It is a yaml text file with three main sections "resource_registry",
|
||||
"parameters" and "parameter_defaults".
|
||||
|
||||
------------------
|
||||
Command line usage
|
||||
@ -81,8 +81,18 @@ Usage examples
|
||||
InstanceType: m1.micro
|
||||
ImageId: F18-x86_64-cfntools
|
||||
|
||||
2) Define defaults to parameters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
This is especially useful when you have many template resources and
|
||||
you want the same value in each. Note: these defaults will get passed
|
||||
down into all template resources.
|
||||
::
|
||||
|
||||
2) Deal with the mapping of Quantum to Neutron
|
||||
parameter_defaults:
|
||||
KeyName: heat_key
|
||||
|
||||
|
||||
3) Deal with the mapping of Quantum to Neutron
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
::
|
||||
|
||||
@ -92,7 +102,7 @@ Usage examples
|
||||
So all existing resources which can be matched with "OS::Neutron*"
|
||||
will be mapped to "OS::Quantum*" accordingly.
|
||||
|
||||
3) Override a resource type with a custom template resource
|
||||
4) Override a resource type with a custom template resource
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
::
|
||||
|
||||
@ -103,7 +113,7 @@ Please note that the template resource URL here must end with ".yaml"
|
||||
or ".template", or it will not be treated as a custom template
|
||||
resource. The supported URL types are "http, https and file".
|
||||
|
||||
4) Always map resource type X to Y
|
||||
5) Always map resource type X to Y
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
::
|
||||
|
||||
@ -111,7 +121,7 @@ resource. The supported URL types are "http, https and file".
|
||||
"OS::Networking::FloatingIP": "OS::Nova::FloatingIP"
|
||||
|
||||
|
||||
5) Use default resources except one for a particular resource in the template
|
||||
6) Use default resources except one for a particular resource in the template
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
::
|
||||
|
||||
|
@ -15,8 +15,8 @@ from heat.common.i18n import _
|
||||
from heat.common import template_format
|
||||
|
||||
|
||||
SECTIONS = (PARAMETERS, RESOURCE_REGISTRY) = \
|
||||
('parameters', 'resource_registry')
|
||||
SECTIONS = (PARAMETERS, RESOURCE_REGISTRY, PARAMETER_DEFAULTS) = \
|
||||
('parameters', 'resource_registry', 'parameter_defaults')
|
||||
|
||||
|
||||
def parse(env_str):
|
||||
|
@ -69,14 +69,20 @@ class CfnTemplate(template.Template):
|
||||
# to be consistent with an empty json section.
|
||||
return self.t.get(section) or default
|
||||
|
||||
def param_schemata(self):
|
||||
def param_schemata(self, param_defaults=None):
|
||||
params = self.t.get(self.PARAMETERS) or {}
|
||||
pdefaults = param_defaults or {}
|
||||
for name, schema in six.iteritems(params):
|
||||
if name in pdefaults:
|
||||
params[name][parameters.DEFAULT] = pdefaults[name]
|
||||
|
||||
return dict((name, parameters.Schema.from_dict(name, schema))
|
||||
for name, schema in six.iteritems(params))
|
||||
|
||||
def parameters(self, stack_identifier, user_params):
|
||||
def parameters(self, stack_identifier, user_params, param_defaults=None):
|
||||
return parameters.Parameters(stack_identifier, self,
|
||||
user_params=user_params)
|
||||
user_params=user_params,
|
||||
param_defaults=param_defaults)
|
||||
|
||||
def resource_definitions(self, stack):
|
||||
def rsrc_defn_item(name, snippet):
|
||||
|
@ -121,8 +121,10 @@ class TemplateResourceInfo(ResourceInfo):
|
||||
|
||||
def get_class(self):
|
||||
from heat.engine.resources import template_resource
|
||||
env = self.registry.environment
|
||||
return template_resource.generate_class(str(self.name),
|
||||
self.template_name)
|
||||
self.template_name,
|
||||
env)
|
||||
|
||||
|
||||
class MapResourceInfo(ResourceInfo):
|
||||
@ -156,9 +158,10 @@ class GlobResourceInfo(MapResourceInfo):
|
||||
class ResourceRegistry(object):
|
||||
"""By looking at the environment, find the resource implementation."""
|
||||
|
||||
def __init__(self, global_registry):
|
||||
def __init__(self, global_registry, env):
|
||||
self._registry = {'resources': {}}
|
||||
self.global_registry = global_registry
|
||||
self.environment = env
|
||||
|
||||
def load(self, json_snippet):
|
||||
self._load_registry([], json_snippet)
|
||||
@ -363,20 +366,28 @@ class Environment(object):
|
||||
else:
|
||||
global_registry = None
|
||||
|
||||
self.registry = ResourceRegistry(global_registry)
|
||||
self.registry = ResourceRegistry(global_registry, self)
|
||||
self.registry.load(env.get(env_fmt.RESOURCE_REGISTRY, {}))
|
||||
|
||||
if env_fmt.PARAMETER_DEFAULTS in env:
|
||||
self.param_defaults = env[env_fmt.PARAMETER_DEFAULTS]
|
||||
else:
|
||||
self.param_defaults = {}
|
||||
|
||||
if env_fmt.PARAMETERS in env:
|
||||
self.params = env[env_fmt.PARAMETERS]
|
||||
else:
|
||||
self.params = dict((k, v) for (k, v) in six.iteritems(env)
|
||||
if k != env_fmt.RESOURCE_REGISTRY)
|
||||
if k not in (env_fmt.PARAMETER_DEFAULTS,
|
||||
env_fmt.RESOURCE_REGISTRY))
|
||||
self.constraints = {}
|
||||
self.stack_lifecycle_plugins = []
|
||||
|
||||
def load(self, env_snippet):
|
||||
self.registry.load(env_snippet.get(env_fmt.RESOURCE_REGISTRY, {}))
|
||||
self.params.update(env_snippet.get(env_fmt.PARAMETERS, {}))
|
||||
self.param_defaults.update(
|
||||
env_snippet.get(env_fmt.PARAMETER_DEFAULTS, {}))
|
||||
|
||||
def patch_previous_parameters(self, previous_env, clear_parameters=[]):
|
||||
"""This instance of Environment is the new environment where
|
||||
@ -394,7 +405,8 @@ class Environment(object):
|
||||
def user_env_as_dict(self):
|
||||
"""Get the environment as a dict, ready for storing in the db."""
|
||||
return {env_fmt.RESOURCE_REGISTRY: self.registry.as_dict(),
|
||||
env_fmt.PARAMETERS: self.params}
|
||||
env_fmt.PARAMETERS: self.params,
|
||||
env_fmt.PARAMETER_DEFAULTS: self.param_defaults}
|
||||
|
||||
def register_class(self, resource_type, resource_class):
|
||||
self.registry.register_class(resource_type, resource_class)
|
||||
@ -425,22 +437,25 @@ class Environment(object):
|
||||
return self.stack_lifecycle_plugins
|
||||
|
||||
|
||||
def get_custom_environment(registry, cust_params):
|
||||
"""Build a customized environment using the given registry and params.
|
||||
This is built from the cust_params and the given registry so some
|
||||
def get_child_environment(parent_env, child_params):
|
||||
"""Build a child environment using the parent environment and params.
|
||||
This is built from the child_params and the parent env so some
|
||||
resources can use user-provided parameters as if they come from an
|
||||
environment.
|
||||
"""
|
||||
new_env = Environment()
|
||||
new_env.registry = registry
|
||||
cust_env = {env_fmt.PARAMETERS: {}}
|
||||
if cust_params is not None:
|
||||
if env_fmt.PARAMETERS not in cust_params:
|
||||
cust_env[env_fmt.PARAMETERS] = cust_params
|
||||
new_env.registry = copy.copy(parent_env.registry)
|
||||
new_env.environment = new_env
|
||||
child_env = {
|
||||
env_fmt.PARAMETERS: {},
|
||||
env_fmt.PARAMETER_DEFAULTS: parent_env.param_defaults}
|
||||
if child_params is not None:
|
||||
if env_fmt.PARAMETERS not in child_params:
|
||||
child_env[env_fmt.PARAMETERS] = child_params
|
||||
else:
|
||||
cust_env.update(cust_params)
|
||||
child_env.update(child_params)
|
||||
|
||||
new_env.load(cust_env)
|
||||
new_env.load(child_env)
|
||||
return new_env
|
||||
|
||||
|
||||
|
@ -171,17 +171,21 @@ class HOTemplate20130523(template.Template):
|
||||
return self._translate_section('outputs', 'value', outputs,
|
||||
HOT_TO_CFN_ATTRS)
|
||||
|
||||
def param_schemata(self):
|
||||
parameter_section = self.t.get(self.PARAMETERS)
|
||||
if parameter_section is None:
|
||||
parameter_section = {}
|
||||
def param_schemata(self, param_defaults=None):
|
||||
parameter_section = self.t.get(self.PARAMETERS) or {}
|
||||
pdefaults = param_defaults or {}
|
||||
for name, schema in six.iteritems(parameter_section):
|
||||
if name in pdefaults:
|
||||
parameter_section[name]['default'] = pdefaults[name]
|
||||
|
||||
params = six.iteritems(parameter_section)
|
||||
return dict((name, parameters.HOTParamSchema.from_dict(name, schema))
|
||||
for name, schema in params)
|
||||
|
||||
def parameters(self, stack_identifier, user_params):
|
||||
def parameters(self, stack_identifier, user_params, param_defaults=None):
|
||||
return parameters.HOTParameters(stack_identifier, self,
|
||||
user_params=user_params)
|
||||
user_params=user_params,
|
||||
param_defaults=param_defaults)
|
||||
|
||||
def resource_definitions(self, stack):
|
||||
allowed_keys = set(_RESOURCE_KEYS)
|
||||
|
@ -27,7 +27,7 @@ class ParameterGroups(object):
|
||||
'''
|
||||
def __init__(self, tmpl):
|
||||
self.tmpl = tmpl
|
||||
self.parameters = tmpl.parameters(None, {})
|
||||
self.parameters = tmpl.parameters(None, {}, param_defaults={})
|
||||
LOG.debug(self.tmpl)
|
||||
LOG.debug(self.parameters)
|
||||
self.parameter_names = []
|
||||
|
@ -207,6 +207,7 @@ class Parameter(object):
|
||||
self.name = name
|
||||
self.schema = schema
|
||||
self.user_value = value
|
||||
self.user_default = None
|
||||
|
||||
def validate(self, validate_value=True, context=None):
|
||||
'''
|
||||
@ -264,11 +265,15 @@ class Parameter(object):
|
||||
|
||||
def has_default(self):
|
||||
'''Return whether the parameter has a default value.'''
|
||||
return self.schema.default is not None
|
||||
return (self.schema.default is not None or
|
||||
self.user_default is not None)
|
||||
|
||||
def default(self):
|
||||
'''Return the default value of the parameter.'''
|
||||
return self.schema.default
|
||||
return self.user_default or self.schema.default
|
||||
|
||||
def set_default(self, value):
|
||||
self.user_default = value
|
||||
|
||||
def __str__(self):
|
||||
'''Return a string representation of the parameter'''
|
||||
@ -421,12 +426,14 @@ class Parameters(collections.Mapping):
|
||||
'AWS::StackId', 'AWS::StackName', 'AWS::Region'
|
||||
)
|
||||
|
||||
def __init__(self, stack_identifier, tmpl, user_params=None):
|
||||
def __init__(self, stack_identifier, tmpl, user_params=None,
|
||||
param_defaults=None):
|
||||
'''
|
||||
Create the parameter container for a stack from the stack name and
|
||||
template, optionally setting the user-supplied parameter values.
|
||||
'''
|
||||
user_params = user_params or {}
|
||||
param_defaults = param_defaults or {}
|
||||
|
||||
def user_parameter(schema_item):
|
||||
name, schema = schema_item
|
||||
@ -445,6 +452,10 @@ class Parameters(collections.Mapping):
|
||||
p) for p in itertools.chain(pseudo_parameters,
|
||||
user_parameters))
|
||||
|
||||
for pd in six.iterkeys(param_defaults):
|
||||
if pd in self.params:
|
||||
self.params[pd].set_default(param_defaults[pd])
|
||||
|
||||
def validate(self, validate_value=True, context=None):
|
||||
'''
|
||||
Validates all parameters.
|
||||
|
@ -135,9 +135,8 @@ class RemoteStack(resource.Resource):
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
try:
|
||||
registry = self.stack.env.registry
|
||||
params = self.properties[self.PARAMETERS]
|
||||
env = environment.get_custom_environment(registry, params)
|
||||
env = environment.get_child_environment(self.stack.env, params)
|
||||
tmpl = template_format.parse(self.properties[self.TEMPLATE])
|
||||
args = {
|
||||
'template': tmpl,
|
||||
@ -153,9 +152,8 @@ class RemoteStack(resource.Resource):
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
def handle_create(self):
|
||||
registry = self.stack.env.registry
|
||||
params = self.properties[self.PARAMETERS]
|
||||
env = environment.get_custom_environment(registry, params)
|
||||
env = environment.get_child_environment(self.stack.env, params)
|
||||
tmpl = template_format.parse(self.properties[self.TEMPLATE])
|
||||
args = {
|
||||
'stack_name': self.physical_resource_name_or_FnGetRefId(),
|
||||
@ -197,8 +195,7 @@ class RemoteStack(resource.Resource):
|
||||
self.name)
|
||||
|
||||
params = self.properties[self.PARAMETERS]
|
||||
registry = self.stack.env.registry
|
||||
env = environment.get_custom_environment(registry, params)
|
||||
env = environment.get_child_environment(self.stack.env, params)
|
||||
tmpl = template_format.parse(self.properties[self.TEMPLATE])
|
||||
fields = {
|
||||
'stack_id': self.resource_id,
|
||||
|
@ -27,10 +27,10 @@ from heat.engine import stack_resource
|
||||
from heat.engine import template
|
||||
|
||||
|
||||
def generate_class(name, template_name):
|
||||
def generate_class(name, template_name, env):
|
||||
data = TemplateResource.get_template_file(template_name, ('file',))
|
||||
tmpl = template.Template(template_format.parse(data))
|
||||
props, attrs = TemplateResource.get_schemas(tmpl)
|
||||
props, attrs = TemplateResource.get_schemas(tmpl, env.param_defaults)
|
||||
cls = type(name, (TemplateResource,),
|
||||
{'properties_schema': props,
|
||||
'attributes_schema': attrs})
|
||||
@ -83,9 +83,9 @@ class TemplateResource(stack_resource.StackResource):
|
||||
raise exception.NotFound(msg_fmt=msg)
|
||||
|
||||
@staticmethod
|
||||
def get_schemas(tmpl):
|
||||
def get_schemas(tmpl, param_defaults):
|
||||
return ((properties.Properties.schema_from_params(
|
||||
tmpl.param_schemata())),
|
||||
tmpl.param_schemata(param_defaults))),
|
||||
(attributes.Attributes.schema_from_outputs(
|
||||
tmpl[tmpl.OUTPUTS])))
|
||||
|
||||
@ -99,7 +99,8 @@ class TemplateResource(stack_resource.StackResource):
|
||||
{"HeatTemplateFormatVersion": "2012-12-12"})
|
||||
|
||||
# re-generate the properties and attributes from the template.
|
||||
self.properties_schema, self.attributes_schema = self.get_schemas(tmpl)
|
||||
self.properties_schema, self.attributes_schema = self.get_schemas(
|
||||
tmpl, self.stack.env.param_defaults)
|
||||
|
||||
self.properties = definition.properties(self.properties_schema,
|
||||
self.context)
|
||||
|
@ -131,8 +131,10 @@ class Stack(collections.Mapping):
|
||||
resources.initialise()
|
||||
|
||||
self.env = env or environment.Environment({})
|
||||
self.parameters = self.t.parameters(self.identifier(),
|
||||
user_params=self.env.params)
|
||||
self.parameters = self.t.parameters(
|
||||
self.identifier(),
|
||||
user_params=self.env.params,
|
||||
param_defaults=self.env.param_defaults)
|
||||
self._set_param_stackid()
|
||||
|
||||
if resolve_data:
|
||||
|
@ -164,13 +164,12 @@ class StackResource(resource.Resource):
|
||||
|
||||
# Note we disable rollback for nested stacks, since they
|
||||
# should be rolled back by the parent stack on failure
|
||||
child_env = environment.get_custom_environment(
|
||||
self.stack.env.registry,
|
||||
child_params)
|
||||
child_env = environment.get_child_environment(
|
||||
self.stack.env, child_params)
|
||||
nested = parser.Stack(self.context,
|
||||
stack_name,
|
||||
parsed_template,
|
||||
child_env,
|
||||
env=child_env,
|
||||
timeout_mins=timeout_mins,
|
||||
disable_rollback=True,
|
||||
parent_resource=self,
|
||||
|
@ -139,12 +139,12 @@ class Template(collections.Mapping):
|
||||
return len(self.SECTIONS) - len(self.SECTIONS_NO_DIRECT_ACCESS)
|
||||
|
||||
@abc.abstractmethod
|
||||
def param_schemata(self):
|
||||
def param_schemata(self, param_defaults=None):
|
||||
'''Return a dict of parameters.Schema objects for the parameters.'''
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def parameters(self, stack_identifier, user_params):
|
||||
def parameters(self, stack_identifier, user_params, param_defaults=None):
|
||||
'''Return a parameters.Parameters object for the stack.'''
|
||||
pass
|
||||
|
||||
|
@ -163,6 +163,7 @@ blarg: wibble
|
||||
def test_parameters(self):
|
||||
params = {'foo': 'bar', 'blarg': 'wibble'}
|
||||
body = {'parameters': params,
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}}
|
||||
data = stacks.InstantiationData(body)
|
||||
self.assertEqual(body, data.environment())
|
||||
@ -178,6 +179,7 @@ blarg: wibble
|
||||
'environment': {'parameters': {'blarg': 'wibble'}}}
|
||||
expect = {'parameters': {'blarg': 'wibble',
|
||||
'foo': 'bar'},
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}}
|
||||
data = stacks.InstantiationData(body)
|
||||
self.assertEqual(expect, data.environment())
|
||||
@ -192,6 +194,7 @@ blarg: wibble
|
||||
expect = {'parameters': {'blarg': 'wibble',
|
||||
'foo': 'bar',
|
||||
'tester': 'Yes'},
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}}
|
||||
data = stacks.InstantiationData(body)
|
||||
self.assertEqual(expect, data.environment())
|
||||
@ -206,7 +209,8 @@ blarg: wibble
|
||||
env = {'foo': 'bar', 'blarg': 'wibble'}
|
||||
body = {'not the environment': env}
|
||||
data = stacks.InstantiationData(body)
|
||||
self.assertEqual({'parameters': {}, 'resource_registry': {}},
|
||||
self.assertEqual({'parameters': {}, 'parameter_defaults': {},
|
||||
'resource_registry': {}},
|
||||
data.environment())
|
||||
|
||||
def test_args(self):
|
||||
@ -710,6 +714,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
{'stack_name': identity.stack_name,
|
||||
'template': template,
|
||||
'params': {'parameters': parameters,
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}},
|
||||
'files': {},
|
||||
'args': {'timeout_mins': 30},
|
||||
@ -770,6 +775,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
{'stack_name': identity.stack_name,
|
||||
'template': template,
|
||||
'params': {'parameters': parameters,
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}},
|
||||
'files': {},
|
||||
'args': {'timeout_mins': 30,
|
||||
@ -835,6 +841,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
{'stack_name': identity.stack_name,
|
||||
'template': template,
|
||||
'params': {'parameters': parameters,
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}},
|
||||
'files': {'my.yaml': 'This is the file contents.'},
|
||||
'args': {'timeout_mins': 30},
|
||||
@ -877,6 +884,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
{'stack_name': stack_name,
|
||||
'template': template,
|
||||
'params': {'parameters': parameters,
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}},
|
||||
'files': {},
|
||||
'args': {'timeout_mins': 30},
|
||||
@ -892,6 +900,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
{'stack_name': stack_name,
|
||||
'template': template,
|
||||
'params': {'parameters': parameters,
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}},
|
||||
'files': {},
|
||||
'args': {'timeout_mins': 30},
|
||||
@ -907,6 +916,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
{'stack_name': stack_name,
|
||||
'template': template,
|
||||
'params': {'parameters': parameters,
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}},
|
||||
'files': {},
|
||||
'args': {'timeout_mins': 30},
|
||||
@ -959,6 +969,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
{'stack_name': stack_name,
|
||||
'template': template,
|
||||
'params': {'parameters': parameters,
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}},
|
||||
'files': {},
|
||||
'args': {'timeout_mins': 30},
|
||||
@ -1017,6 +1028,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
{'stack_name': stack_name,
|
||||
'template': template,
|
||||
'params': {'parameters': parameters,
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}},
|
||||
'files': {},
|
||||
'args': {'timeout_mins': 30},
|
||||
@ -1415,6 +1427,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
{'stack_identity': dict(identity),
|
||||
'template': template,
|
||||
'params': {'parameters': parameters,
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}},
|
||||
'files': {},
|
||||
'args': {'timeout_mins': 30}})
|
||||
@ -1450,6 +1463,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
{'stack_identity': dict(identity),
|
||||
'template': template,
|
||||
'params': {u'parameters': parameters,
|
||||
u'parameter_defaults': {},
|
||||
u'resource_registry': {}},
|
||||
'files': {},
|
||||
'args': {'timeout_mins': 30}})
|
||||
@ -1509,6 +1523,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
{'stack_identity': dict(identity),
|
||||
'template': template,
|
||||
'params': {'parameters': {},
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}},
|
||||
'files': {},
|
||||
'args': {rpc_api.PARAM_EXISTING: True,
|
||||
@ -1544,6 +1559,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
{'stack_identity': dict(identity),
|
||||
'template': template,
|
||||
'params': {'parameters': parameters,
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}},
|
||||
'files': {},
|
||||
'args': {rpc_api.PARAM_EXISTING: True,
|
||||
@ -1581,6 +1597,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
{'stack_identity': dict(identity),
|
||||
'template': template,
|
||||
'params': {'parameters': {},
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}},
|
||||
'files': {},
|
||||
'args': {rpc_api.PARAM_EXISTING: True,
|
||||
@ -1620,6 +1637,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
{'stack_identity': dict(identity),
|
||||
'template': template,
|
||||
'params': {'parameters': parameters,
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}},
|
||||
'files': {},
|
||||
'args': {rpc_api.PARAM_EXISTING: True,
|
||||
@ -1757,6 +1775,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
('validate_template',
|
||||
{'template': template,
|
||||
'params': {'parameters': {},
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}}})
|
||||
).AndReturn(engine_response)
|
||||
self.m.ReplayAll()
|
||||
@ -1780,6 +1799,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
('validate_template',
|
||||
{'template': template,
|
||||
'params': {'parameters': {},
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {}}})
|
||||
).AndReturn({'Error': 'fubar'})
|
||||
self.m.ReplayAll()
|
||||
|
@ -2286,7 +2286,8 @@ class StackServiceTest(common.HeatTestCase):
|
||||
self.assertEqual(expected, schema)
|
||||
|
||||
def _no_template_file(self, function):
|
||||
info = environment.ResourceInfo(environment.ResourceRegistry,
|
||||
env = environment.Environment()
|
||||
info = environment.ResourceInfo(env.registry,
|
||||
['ResourceWithWrongRefOnFile'],
|
||||
'not_existing.yaml')
|
||||
mock_iterable = mock.MagicMock(return_value=iter([info]))
|
||||
|
@ -36,12 +36,14 @@ class EnvironmentTest(common.HeatTestCase):
|
||||
def test_load_old_parameters(self):
|
||||
old = {u'a': u'ff', u'b': u'ss'}
|
||||
expected = {u'parameters': old,
|
||||
u'parameter_defaults': {},
|
||||
u'resource_registry': {u'resources': {}}}
|
||||
env = environment.Environment(old)
|
||||
self.assertEqual(expected, env.user_env_as_dict())
|
||||
|
||||
def test_load_new_env(self):
|
||||
new_env = {u'parameters': {u'a': u'ff', u'b': u'ss'},
|
||||
u'parameter_defaults': {u'ff': 'new_def'},
|
||||
u'resource_registry': {u'OS::Food': u'fruity.yaml',
|
||||
u'resources': {}}}
|
||||
env = environment.Environment(new_env)
|
||||
@ -52,6 +54,7 @@ class EnvironmentTest(common.HeatTestCase):
|
||||
prev_params = {'foo': 'bar', 'tester': 'Yes'}
|
||||
params = {}
|
||||
expected = {'parameters': prev_params,
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {'resources': {}}}
|
||||
prev_env = environment.Environment(
|
||||
{'parameters': prev_params,
|
||||
@ -65,6 +68,7 @@ class EnvironmentTest(common.HeatTestCase):
|
||||
prev_params = {'foo': 'bar', 'tester': 'Yes'}
|
||||
params = {'tester': 'patched'}
|
||||
expected = {'parameters': {'foo': 'bar', 'tester': 'patched'},
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {'resources': {}}}
|
||||
prev_env = environment.Environment(
|
||||
{'parameters': prev_params,
|
||||
@ -79,6 +83,7 @@ class EnvironmentTest(common.HeatTestCase):
|
||||
'another_tester': 'Yes'}
|
||||
params = {'tester': 'patched'}
|
||||
expected = {'parameters': {'foo': 'bar', 'tester': 'patched'},
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {'resources': {}}}
|
||||
prev_env = environment.Environment(
|
||||
{'parameters': prev_params,
|
||||
@ -92,6 +97,7 @@ class EnvironmentTest(common.HeatTestCase):
|
||||
prev_params = {'foo': 'bar', 'tester': 'Yes'}
|
||||
params = {}
|
||||
expected = {'parameters': {'foo': 'bar'},
|
||||
'parameter_defaults': {},
|
||||
'resource_registry': {'resources': {}}}
|
||||
prev_env = environment.Environment(
|
||||
{'parameters': prev_params,
|
||||
|
@ -24,6 +24,7 @@ class YamlEnvironmentTest(common.HeatTestCase):
|
||||
yaml1 = ''
|
||||
yaml2 = '''
|
||||
parameters: {}
|
||||
parameter_defaults: {}
|
||||
resource_registry: {}
|
||||
'''
|
||||
tpl1 = environment_format.parse(yaml1)
|
||||
|
@ -392,13 +392,14 @@ params_schema = json.loads('''{
|
||||
|
||||
class ParametersTest(testtools.TestCase):
|
||||
def new_parameters(self, stack_name, tmpl, user_params=None,
|
||||
stack_id=None, validate_value=True):
|
||||
stack_id=None, validate_value=True,
|
||||
param_defaults=None):
|
||||
user_params = user_params or {}
|
||||
tmpl.update({'HeatTemplateFormatVersion': '2012-12-12'})
|
||||
tmpl = template.Template(tmpl)
|
||||
params = tmpl.parameters(
|
||||
identifier.HeatIdentifier('', stack_name, stack_id),
|
||||
user_params)
|
||||
user_params, param_defaults=param_defaults)
|
||||
params.validate(validate_value)
|
||||
return params
|
||||
|
||||
@ -505,6 +506,21 @@ class ParametersTest(testtools.TestCase):
|
||||
'test',
|
||||
params)
|
||||
|
||||
def test_use_user_default(self):
|
||||
template = {'Parameters': {'a': {'Type': 'Number', 'Default': '42'}}}
|
||||
params = self.new_parameters('test_params', template,
|
||||
param_defaults={'a': '77'})
|
||||
|
||||
self.assertEqual(77, params['a'])
|
||||
|
||||
def test_dont_use_user_default(self):
|
||||
template = {'Parameters': {'a': {'Type': 'Number', 'Default': '42'}}}
|
||||
params = self.new_parameters('test_params', template,
|
||||
{'a': '111'},
|
||||
param_defaults={'a': '77'})
|
||||
|
||||
self.assertEqual(111, params['a'])
|
||||
|
||||
|
||||
class ParameterSchemaTest(testtools.TestCase):
|
||||
|
||||
|
@ -285,8 +285,8 @@ class RemoteStackTest(tests_common.HeatTestCase):
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
self.assertEqual('c8a19429-7fde-47ea-a42f-40045488226c',
|
||||
rsrc.resource_id)
|
||||
registry = rsrc.stack.env.registry
|
||||
env = environment.get_custom_environment(registry, {'name': 'foo'})
|
||||
env = environment.get_child_environment(rsrc.stack.env,
|
||||
{'name': 'foo'})
|
||||
args = {
|
||||
'stack_name': rsrc.physical_resource_name(),
|
||||
'template': template_format.parse(remote_template),
|
||||
@ -520,8 +520,8 @@ class RemoteStackTest(tests_common.HeatTestCase):
|
||||
|
||||
self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state)
|
||||
self.assertEqual('bar', rsrc.properties.get('parameters')['name'])
|
||||
registry = rsrc.stack.env.registry
|
||||
env = environment.get_custom_environment(registry, {'name': 'bar'})
|
||||
env = environment.get_child_environment(rsrc.stack.env,
|
||||
{'name': 'bar'})
|
||||
fields = {
|
||||
'stack_id': rsrc.resource_id,
|
||||
'template': template_format.parse(remote_template),
|
||||
|
@ -210,7 +210,7 @@ class StackResourceTest(common.HeatTestCase):
|
||||
self.stack = self.parent_resource.nested()
|
||||
self.assertEqual({"foo": "bar"}, self.stack.t.files)
|
||||
|
||||
@mock.patch('heat.engine.environment.get_custom_environment')
|
||||
@mock.patch('heat.engine.environment.get_child_environment')
|
||||
@mock.patch.object(stack_resource.parser, 'Stack')
|
||||
def test_preview_with_implemented_child_resource(self, mock_stack_class,
|
||||
mock_env_class):
|
||||
@ -231,14 +231,14 @@ class StackResourceTest(common.HeatTestCase):
|
||||
parent_resource._validate_nested_resources = validation_mock
|
||||
|
||||
result = parent_resource.preview()
|
||||
mock_env_class.assert_called_once_with(self.parent_stack.env.registry,
|
||||
mock_env_class.assert_called_once_with(self.parent_stack.env,
|
||||
params)
|
||||
self.assertEqual('preview_nested_stack', result)
|
||||
mock_stack_class.assert_called_once_with(
|
||||
mock.ANY,
|
||||
'test_stack-test',
|
||||
mock.ANY,
|
||||
'environment',
|
||||
env='environment',
|
||||
timeout_mins=None,
|
||||
disable_rollback=True,
|
||||
parent_resource=parent_resource,
|
||||
@ -249,7 +249,7 @@ class StackResourceTest(common.HeatTestCase):
|
||||
nested_depth=1
|
||||
)
|
||||
|
||||
@mock.patch('heat.engine.environment.get_custom_environment')
|
||||
@mock.patch('heat.engine.environment.get_child_environment')
|
||||
@mock.patch.object(stack_resource.parser, 'Stack')
|
||||
def test_preview_with_implemented_dict_child_resource(self,
|
||||
mock_stack_class,
|
||||
@ -271,14 +271,14 @@ class StackResourceTest(common.HeatTestCase):
|
||||
parent_resource._validate_nested_resources = validation_mock
|
||||
|
||||
result = parent_resource.preview()
|
||||
mock_env_class.assert_called_once_with(self.parent_stack.env.registry,
|
||||
mock_env_class.assert_called_once_with(self.parent_stack.env,
|
||||
params)
|
||||
self.assertEqual('preview_nested_stack', result)
|
||||
mock_stack_class.assert_called_once_with(
|
||||
mock.ANY,
|
||||
'test_stack-test',
|
||||
mock.ANY,
|
||||
'environment',
|
||||
env='environment',
|
||||
timeout_mins=None,
|
||||
disable_rollback=True,
|
||||
parent_resource=parent_resource,
|
||||
@ -810,7 +810,7 @@ class StackResourceTest(common.HeatTestCase):
|
||||
environment.Environment().AndReturn(env)
|
||||
|
||||
self.m.StubOutWithMock(parser, 'Stack')
|
||||
parser.Stack(ctx, phy_id, templ, env, timeout_mins=None,
|
||||
parser.Stack(ctx, phy_id, templ, env=env, timeout_mins=None,
|
||||
disable_rollback=True,
|
||||
parent_resource=self.parent_resource,
|
||||
owner_id=self.parent_stack.id,
|
||||
|
@ -17,6 +17,7 @@ import random
|
||||
import re
|
||||
import six
|
||||
import subprocess
|
||||
import testscenarios
|
||||
import testtools
|
||||
import time
|
||||
|
||||
@ -63,7 +64,8 @@ def rand_name(name=''):
|
||||
return randbits
|
||||
|
||||
|
||||
class HeatIntegrationTest(testtools.TestCase):
|
||||
class HeatIntegrationTest(testscenarios.WithScenarios,
|
||||
testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(HeatIntegrationTest, self).setUp()
|
||||
|
107
heat_integrationtests/functional/test_default_parameters.py
Normal file
107
heat_integrationtests/functional/test_default_parameters.py
Normal file
@ -0,0 +1,107 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import yaml
|
||||
|
||||
from heat_integrationtests.common import test
|
||||
|
||||
|
||||
class DefaultParametersTest(test.HeatIntegrationTest):
|
||||
|
||||
template = '''
|
||||
heat_template_version: 2013-05-23
|
||||
parameters:
|
||||
length:
|
||||
type: string
|
||||
default: 40
|
||||
resources:
|
||||
random1:
|
||||
type: nested_random.yaml
|
||||
random2:
|
||||
type: OS::Heat::RandomString
|
||||
properties:
|
||||
length: {get_param: length}
|
||||
outputs:
|
||||
random1:
|
||||
value: {get_attr: [random1, random1_value]}
|
||||
random2:
|
||||
value: {get_resource: random2}
|
||||
'''
|
||||
nested_template = '''
|
||||
heat_template_version: 2013-05-23
|
||||
parameters:
|
||||
length:
|
||||
type: string
|
||||
default: 50
|
||||
resources:
|
||||
random1:
|
||||
type: OS::Heat::RandomString
|
||||
properties:
|
||||
length: {get_param: length}
|
||||
outputs:
|
||||
random1_value:
|
||||
value: {get_resource: random1}
|
||||
'''
|
||||
|
||||
scenarios = [
|
||||
('none', dict(param=None, default=None, temp_def=True,
|
||||
expect1=50, expect2=40)),
|
||||
('default', dict(param=None, default=12, temp_def=True,
|
||||
expect1=12, expect2=12)),
|
||||
('both', dict(param=15, default=12, temp_def=True,
|
||||
expect1=12, expect2=15)),
|
||||
('no_temp_default', dict(param=None, default=12, temp_def=False,
|
||||
expect1=12, expect2=12)),
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(DefaultParametersTest, self).setUp()
|
||||
self.client = self.orchestration_client
|
||||
|
||||
def test_defaults(self):
|
||||
stack_name = self._stack_rand_name()
|
||||
|
||||
env = {'parameters': {}, 'parameter_defaults': {}}
|
||||
if self.param:
|
||||
env['parameters'] = {'length': self.param}
|
||||
if self.default:
|
||||
env['parameter_defaults'] = {'length': self.default}
|
||||
|
||||
if not self.temp_def:
|
||||
# remove the default from the parameter in the nested template.
|
||||
ntempl = yaml.load(self.nested_template)
|
||||
del ntempl['parameters']['length']['default']
|
||||
nested_template = yaml.dump(ntempl)
|
||||
else:
|
||||
nested_template = self.nested_template
|
||||
|
||||
self.client.stacks.create(
|
||||
stack_name=stack_name,
|
||||
template=self.template,
|
||||
files={'nested_random.yaml': nested_template},
|
||||
disable_rollback=True,
|
||||
parameters={},
|
||||
environment=env
|
||||
)
|
||||
self.addCleanup(self.client.stacks.delete, stack_name)
|
||||
|
||||
stack = self.client.stacks.get(stack_name)
|
||||
stack_identifier = '%s/%s' % (stack_name, stack.id)
|
||||
|
||||
self._wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
|
||||
|
||||
stack = self.client.stacks.get(stack_name)
|
||||
for out in stack.outputs:
|
||||
if out['output_key'] == 'random1':
|
||||
self.assertEqual(self.expect1, len(out['output_value']))
|
||||
if out['output_key'] == 'random2':
|
||||
self.assertEqual(self.expect2, len(out['output_value']))
|
Loading…
Reference in New Issue
Block a user