From 83cd934beb2f8657f59d2e7219b71f8b8df5a657 Mon Sep 17 00:00:00 2001 From: Steve Baker Date: Wed, 5 Jun 2013 13:41:36 +1200 Subject: [PATCH] Add Fn::Replace template function. This is implemented with python str.replace For yaml templates, this should be used as a better alternative to Fn::Join: ['', [...]] For json templates, the following common pattern: "Fn::Join": ["", ["line 1\n", ... "line 2\n"]] could now become more maintainable as: "Fn::Replace": [{...}, "Fn::Join": ["\n", ["line 1", ... "line 2"]]] Implements blueprint template-string-function Implements blueprint bash-environment-function Change-Id: Ie4817b1d98dd6d6390e086da9b582d33415a0d9f --- heat/engine/parser.py | 1 + heat/engine/template.py | 39 ++++++++++++++++++++++++++ heat/tests/test_parser.py | 58 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+) diff --git a/heat/engine/parser.py b/heat/engine/parser.py index 6902443b..f0148a6b 100644 --- a/heat/engine/parser.py +++ b/heat/engine/parser.py @@ -575,6 +575,7 @@ def resolve_runtime_data(template, resources, snippet): resources=resources), template.resolve_select, template.resolve_joins, + template.resolve_replace, template.resolve_base64]) diff --git a/heat/engine/template.py b/heat/engine/template.py index 4bee0ea6..15c6ab78 100644 --- a/heat/engine/template.py +++ b/heat/engine/template.py @@ -279,6 +279,45 @@ class Template(collections.Mapping): return _resolve(lambda k, v: k == 'Fn::Join', handle_join, s) + @staticmethod + def resolve_replace(s): + """ + Resolve constructs of the form. + {"Fn::Replace": [ + {'$var1': 'foo', '%var2%': 'bar'}, + '$var1 is %var2%' + ]} + This is implemented using python str.replace on each key + """ + def handle_replace(args): + if not isinstance(args, (list, tuple)): + raise TypeError('Arguments to "Fn::Replace" must be a list') + + try: + mapping, string = args + except ValueError as ex: + example = ('{"Fn::Replace": ' + '[ {"$var1": "foo", "%var2%": "bar"}, ' + '"$var1 is %var2%"]}') + raise ValueError( + 'Incorrect arguments to "Fn::Replace" %s: %s' % + ('should be', example)) + + if not isinstance(mapping, dict): + raise TypeError( + 'Arguments to "Fn::Replace" not fully resolved') + if not isinstance(string, basestring): + raise TypeError( + 'Arguments to "Fn::Replace" not fully resolved') + + for k, v in mapping.items(): + if v is None: + v = '' + string = string.replace(k, v) + return string + + return _resolve(lambda k, v: k == 'Fn::Replace', handle_replace, s) + @staticmethod def resolve_base64(s): ''' diff --git a/heat/tests/test_parser.py b/heat/tests/test_parser.py index dcdff309..66e9605c 100644 --- a/heat/tests/test_parser.py +++ b/heat/tests/test_parser.py @@ -361,6 +361,64 @@ class TemplateTest(HeatTestCase): parser.Template.resolve_availability_zones(snippet, stack), ["nova1"]) + def test_replace(self): + snippet = {"Fn::Replace": [ + {'$var1': 'foo', '%var2%': 'bar'}, + '$var1 is %var2%' + ]} + self.assertEqual( + parser.Template.resolve_replace(snippet), + 'foo is bar') + + def test_replace_list_mapping(self): + snippet = {"Fn::Replace": [ + ['var1', 'foo', 'var2', 'bar'], + '$var1 is ${var2}' + ]} + self.assertRaises(TypeError, parser.Template.resolve_replace, + snippet) + + def test_replace_dict(self): + snippet = {"Fn::Replace": {}} + self.assertRaises(TypeError, parser.Template.resolve_replace, + snippet) + + def test_replace_missing_template(self): + snippet = {"Fn::Replace": [['var1', 'foo', 'var2', 'bar']]} + self.assertRaises(ValueError, parser.Template.resolve_replace, + snippet) + + def test_replace_none_template(self): + snippet = {"Fn::Replace": [['var1', 'foo', 'var2', 'bar'], None]} + self.assertRaises(TypeError, parser.Template.resolve_replace, + snippet) + + def test_replace_list_string(self): + snippet = {"Fn::Replace": [ + {'var1': 'foo', 'var2': 'bar'}, + ['$var1 is ${var2}'] + ]} + self.assertRaises(TypeError, parser.Template.resolve_replace, + snippet) + + def test_replace_none_values(self): + snippet = {"Fn::Replace": [ + {'$var1': None, '${var2}': None}, + '"$var1" is "${var2}"' + ]} + self.assertEqual( + parser.Template.resolve_replace(snippet), + '"" is ""') + + def test_replace_missing_key(self): + snippet = {"Fn::Replace": [ + {'$var1': 'foo', 'var2': 'bar'}, + '"$var1" is "${var3}"' + ]} + self.assertEqual( + parser.Template.resolve_replace(snippet), + '"foo" is "${var3}"') + class StackTest(HeatTestCase): def setUp(self): -- 2.45.2