diff options
author | Giulio Fidente <gfidente@redhat.com> | 2016-08-12 16:00:03 -0400 |
---|---|---|
committer | Giulio Fidente <gfidente@redhat.com> | 2016-08-16 15:31:59 +0200 |
commit | a6878eea9b4172081934a2bc49b2a73f6fa36466 (patch) | |
tree | 0197503206f5843e2a1e14c420809c007b363dd7 | |
parent | 7216e1d0f6bf1796c4cca1fb09308aaeb89786da (diff) | |
download | heat-a6878eea9b4172081934a2bc49b2a73f6fa36466.tar.gz |
Perform str_replace trying to match longest string first
Purpose is to avoid str_replace from doing partial replace
when a shorter instance of a matching key is found.
Change-Id: I63c13f4eb3b1375cb2df1a34fd4f09561564f400
Co-Authored-By: Zane Bitter <zbitter@redhat.com>
Closes-Bug: 1600209
(cherry picked from commit a2f5b5cb9ae74fb3262948b0cf1c23fc04c4b7cb)
-rw-r--r-- | heat/engine/cfn/functions.py | 8 | ||||
-rw-r--r-- | heat/engine/hot/functions.py | 30 | ||||
-rw-r--r-- | heat/tests/test_hot.py | 20 |
3 files changed, 51 insertions, 7 deletions
diff --git a/heat/engine/cfn/functions.py b/heat/engine/cfn/functions.py index 4ed1ea4b2..48101d25c 100644 --- a/heat/engine/cfn/functions.py +++ b/heat/engine/cfn/functions.py @@ -393,8 +393,9 @@ class Replace(function.Function): "<value_1> <value_2>" - This is implemented using python str.replace on each key. The order in - which replacements are performed is undefined. + This is implemented using python str.replace on each key. Longer keys are + substituted before shorter ones, but the order in which replacements are + performed is otherwise undefined. """ def __init__(self, stack, fn_name, args): @@ -454,6 +455,9 @@ class Replace(function.Function): return string.replace(placeholder, six.text_type(value)) + mapping = collections.OrderedDict(sorted(mapping.items(), + key=lambda t: len(t[0]), + reverse=True)) return six.moves.reduce(replace, six.iteritems(mapping), template) diff --git a/heat/engine/hot/functions.py b/heat/engine/hot/functions.py index b4ae00655..83e7e9601 100644 --- a/heat/engine/hot/functions.py +++ b/heat/engine/hot/functions.py @@ -247,8 +247,9 @@ class Replace(cfn_funcs.Replace): "<value_1> <value_2>" - This is implemented using Python's str.replace on each key. The order in - which replacements are performed is undefined. + This is implemented using python str.replace on each key. Longer keys are + substituted before shorter ones, but the order in which replacements are + performed is otherwise undefined. """ def _parse_args(self): @@ -274,9 +275,25 @@ class Replace(cfn_funcs.Replace): class ReplaceJson(Replace): """A function for performing string substitutions. - Behaves the same as Replace, but tolerates non-string parameter - values, e.g map/list - these are serialized as json before doing - the string substitution. + Takes the form:: + + str_replace: + template: <key_1> <key_2> + params: + <key_1>: <value_1> + <key_2>: <value_2> + ... + + And resolves to:: + + "<value_1> <value_2>" + + This is implemented using python str.replace on each key. Longer keys are + substituted before shorter ones, but the order in which replacements are + performed is otherwise undefined. + + Non-string param values (e.g maps or lists) are serialized as JSON before + being substituted in. """ def result(self): @@ -318,6 +335,9 @@ class ReplaceJson(Replace): return string.replace(placeholder, six.text_type(value)) + mapping = collections.OrderedDict(sorted(mapping.items(), + key=lambda t: len(t[0]), + reverse=True)) return six.moves.reduce(replace, six.iteritems(mapping), template) diff --git a/heat/tests/test_hot.py b/heat/tests/test_hot.py index ec606c48f..3a029a12f 100644 --- a/heat/tests/test_hot.py +++ b/heat/tests/test_hot.py @@ -562,6 +562,26 @@ class HOTemplateTest(common.HeatTestCase): self.assertEqual(snippet_resolved, self.resolve(snippet, tmpl)) + def test_str_replace_order(self, tpl=hot_tpl_empty): + """Test str_replace function substitution order.""" + + snippet = {'str_replace': {'template': '1234567890', + 'params': {'1': 'a', + '12': 'b', + '123': 'c', + '1234': 'd', + '12345': 'e', + '123456': 'f', + '1234567': 'g'}}} + + tmpl = template.Template(tpl) + + self.assertEqual('g890', self.resolve(snippet, tmpl)) + + def test_str_replace_liberty_order(self): + """Test str_replace function substitution order.""" + self.test_str_replace_order(hot_liberty_tpl_empty) + def test_str_replace_syntax(self): """Test str_replace function syntax. |