Merge "Implement HOT intrinsic function get_file"
This commit is contained in:
commit
e00c41b48d
@ -637,3 +637,45 @@ In the example above, one can imagine that MySQL is being configured on a
|
||||
compute instance and the root password is going to be set based on a user
|
||||
provided parameter. The script for doing this is provided as userdata to the
|
||||
compute instance, leveraging the str_replace function.
|
||||
|
||||
get_file
|
||||
------------
|
||||
The *get_file* function allows string content to be substituted into the
|
||||
template. It is generally used as a file inclusion mechanism for files
|
||||
containing non-heat scripts or configuration files.
|
||||
The syntax of the get_file function is as follows:
|
||||
|
||||
::
|
||||
|
||||
get_file: <content key>
|
||||
|
||||
The *content key* will be used to look up the files dictionary that is
|
||||
provided in the REST API call. The *heat* client command from
|
||||
python-heatclient is *get_file* aware and will populate the *files* with
|
||||
the actual content of fetched paths and URLs. The *heat* client command
|
||||
supports relative paths and will transform these to absolute URLs which
|
||||
will be used as the *content key* in the files dictionary.
|
||||
|
||||
The example below demonstrates *get_file* usage with both relative and
|
||||
absolute URLs.
|
||||
|
||||
::
|
||||
|
||||
resources:
|
||||
my_instance:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
# general properties ...
|
||||
user_data:
|
||||
get_file: my_instance_user_data.sh
|
||||
my_other_instance:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
# general properties ...
|
||||
user_data:
|
||||
get_file: http://example.com/my_other_instance_user_data.sh
|
||||
|
||||
If this template was launched from a local file this would result in
|
||||
a *files* dictionary containing entries with keys
|
||||
*file:///path/to/my_instance_user_data.sh* and
|
||||
*http://example.com/my_other_instance_user_data.sh*.
|
||||
|
@ -288,6 +288,32 @@ class HOTemplate(template.Template):
|
||||
return template._resolve(match_str_replace,
|
||||
handle_str_replace, s, transform)
|
||||
|
||||
def resolve_get_file(self, s, transform=None):
|
||||
"""
|
||||
Resolve file inclusion via function get_file. For any key provided
|
||||
the contents of the value in the template files dictionary
|
||||
will be substituted.
|
||||
|
||||
Resolves the get_file function of the form::
|
||||
|
||||
get_file:
|
||||
<string key>
|
||||
"""
|
||||
|
||||
def handle_get_file(args):
|
||||
if not (isinstance(args, basestring)):
|
||||
raise TypeError(
|
||||
_('Argument to "get_file" must be a string'))
|
||||
f = self.files.get(args)
|
||||
if f is None:
|
||||
raise ValueError(_('No content found in the "files" section '
|
||||
'for get_file path: %s') % args)
|
||||
return f
|
||||
|
||||
match_get_file = lambda k, v: k == 'get_file'
|
||||
return template._resolve(match_get_file,
|
||||
handle_get_file, s, transform)
|
||||
|
||||
def param_schemata(self):
|
||||
params = self.t.get(self.PARAMETERS, {}).iteritems()
|
||||
return dict((name, HOTParamSchema.from_dict(schema))
|
||||
|
@ -770,7 +770,8 @@ def resolve_static_data(template, stack, parameters, snippet):
|
||||
functools.partial(template.resolve_resource_facade,
|
||||
stack=stack),
|
||||
template.resolve_find_in_map,
|
||||
template.reduce_joins])
|
||||
template.reduce_joins,
|
||||
template.resolve_get_file])
|
||||
|
||||
|
||||
def resolve_runtime_data(template, resources, snippet):
|
||||
|
@ -481,6 +481,13 @@ class Template(collections.Mapping):
|
||||
handle_resource_facade,
|
||||
s, transform)
|
||||
|
||||
@staticmethod
|
||||
def resolve_get_file(s, transform=None):
|
||||
# cfn templates do not have any analog to get_file so this function
|
||||
# should remain not implemented. Attempts to use get_file in a cfn
|
||||
# template will be passed through with no modification.
|
||||
return s
|
||||
|
||||
def param_schemata(self):
|
||||
params = self.t.get(self.PARAMETERS, {}).iteritems()
|
||||
return dict((name, parameters.Schema.from_dict(schema))
|
||||
|
@ -187,6 +187,57 @@ class HOTemplateTest(HeatTestCase):
|
||||
|
||||
self.assertRaises(TypeError, tmpl.resolve_replace, snippet)
|
||||
|
||||
def test_get_file(self):
|
||||
"""Test get_file function."""
|
||||
|
||||
snippet = {'get_file': 'file:///tmp/foo.yaml'}
|
||||
snippet_resolved = 'foo contents'
|
||||
|
||||
tmpl = parser.Template(hot_tpl_empty, files={
|
||||
'file:///tmp/foo.yaml': 'foo contents'
|
||||
})
|
||||
|
||||
self.assertEqual(snippet_resolved, tmpl.resolve_get_file(snippet))
|
||||
|
||||
def test_get_file_not_string(self):
|
||||
"""Test get_file function with non-string argument."""
|
||||
|
||||
snippet = {'get_file': ['file:///tmp/foo.yaml']}
|
||||
tmpl = parser.Template(hot_tpl_empty)
|
||||
notStrErr = self.assertRaises(
|
||||
TypeError, tmpl.resolve_get_file, snippet)
|
||||
self.assertEqual(
|
||||
'Argument to "get_file" must be a string',
|
||||
str(notStrErr))
|
||||
|
||||
def test_get_file_missing_files(self):
|
||||
"""Test get_file function with no matching key in files section."""
|
||||
|
||||
snippet = {'get_file': 'file:///tmp/foo.yaml'}
|
||||
|
||||
tmpl = parser.Template(hot_tpl_empty, files={
|
||||
'file:///tmp/bar.yaml': 'bar contents'
|
||||
})
|
||||
|
||||
missingErr = self.assertRaises(
|
||||
ValueError, tmpl.resolve_get_file, snippet)
|
||||
self.assertEqual(
|
||||
('No content found in the "files" section for '
|
||||
'get_file path: file:///tmp/foo.yaml'),
|
||||
str(missingErr))
|
||||
|
||||
def test_get_file_nested_does_not_resolve(self):
|
||||
"""Test get_file function does not resolve nested calls."""
|
||||
snippet = {'get_file': 'file:///tmp/foo.yaml'}
|
||||
snippet_resolved = '{get_file: file:///tmp/bar.yaml}'
|
||||
|
||||
tmpl = parser.Template(hot_tpl_empty, files={
|
||||
'file:///tmp/foo.yaml': snippet_resolved,
|
||||
'file:///tmp/bar.yaml': 'bar content',
|
||||
})
|
||||
|
||||
self.assertEqual(snippet_resolved, tmpl.resolve_get_file(snippet))
|
||||
|
||||
def test_prevent_parameters_access(self):
|
||||
"""
|
||||
Test that the parameters section can't be accesed using the template
|
||||
|
Loading…
x
Reference in New Issue
Block a user