summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiulio Fidente <gfidente@redhat.com>2016-08-12 16:00:03 -0400
committerGiulio Fidente <gfidente@redhat.com>2016-08-16 15:31:59 +0200
commita6878eea9b4172081934a2bc49b2a73f6fa36466 (patch)
tree0197503206f5843e2a1e14c420809c007b363dd7
parent7216e1d0f6bf1796c4cca1fb09308aaeb89786da (diff)
downloadheat-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.py8
-rw-r--r--heat/engine/hot/functions.py30
-rw-r--r--heat/tests/test_hot.py20
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.