summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhuangtianhua <huangtianhua@huawei.com>2016-09-09 17:08:00 -0400
committerZane Bitter <zbitter@redhat.com>2016-09-12 21:40:06 -0400
commit4a92678f189e02963302121a814bf17797d6d684 (patch)
tree1173488274fa1910e727e333bac6ddda2632ce80
parentb67605de2478b303f1fa8a40c768698ede35630a (diff)
downloadheat-4a92678f189e02963302121a814bf17797d6d684.tar.gz
Allows condition name using boolean or function
This change supports: 1. Allow boolean value or condition function as condition name of 'if' function: resources: r1: ... properties: a: {if: [true, 'value_true', 'value_false']} r2: ... properties: b: {if: [{equals: [...]}, 'value_true', 'value_false']} 2. Allow boolean value or condition function as condtiion name in resource definition: resources: r1: ... condition: false r2: ... condition: {and: [cd1, cd2]} 3. Allow boolean value or condition function as condition name in outputs: outputs: output1: value: ... condition: true output2: value: ... condition: {not: cd3} Change-Id: I2bf9bb0b608788c928d12425cbfdaf658df9e880 Co-Authored-By: Zane Bitter <zbitter@redhat.com> Blueprint: support-conditions-function
-rw-r--r--doc/source/template_guide/hot_spec.rst4
-rw-r--r--heat/engine/cfn/template.py13
-rw-r--r--heat/engine/conditions.py3
-rw-r--r--heat/engine/hot/functions.py13
-rw-r--r--heat/engine/hot/template.py13
-rw-r--r--heat/engine/rsrc_defn.py9
-rw-r--r--heat/engine/template_common.py7
-rw-r--r--heat/tests/test_hot.py29
-rw-r--r--heat/tests/test_template.py36
9 files changed, 102 insertions, 25 deletions
diff --git a/doc/source/template_guide/hot_spec.rst b/doc/source/template_guide/hot_spec.rst
index 19ddd09bd..509f464b4 100644
--- a/doc/source/template_guide/hot_spec.rst
+++ b/doc/source/template_guide/hot_spec.rst
@@ -644,7 +644,7 @@ the following syntax
update_policy: <update policy>
deletion_policy: <deletion policy>
external_id: <external resource ID>
- condition: <condition name>
+ condition: <condition name or expression or boolean>
resource ID
A resource ID which must be unique within the ``resources`` section of the
@@ -775,7 +775,7 @@ according to the following syntax
<parameter name>:
description: <description>
value: <parameter value>
- condition: <condition name>
+ condition: <condition name or expression or boolean>
parameter name
The output parameter name, which must be unique within the ``outputs``
diff --git a/heat/engine/cfn/template.py b/heat/engine/cfn/template.py
index 0b43cdff1..afb2f55b8 100644
--- a/heat/engine/cfn/template.py
+++ b/heat/engine/cfn/template.py
@@ -11,11 +11,14 @@
# License for the specific language governing permissions and limitations
# under the License.
+import functools
+
import six
from heat.common import exception
from heat.common.i18n import _
from heat.engine.cfn import functions as cfn_funcs
+from heat.engine import function
from heat.engine import parameters
from heat.engine import rsrc_defn
from heat.engine import template_common
@@ -121,7 +124,7 @@ class CfnTemplateBase(template_common.CommonTemplate):
raise exception.StackValidationFailed(message=msg)
defn = rsrc_defn.ResourceDefinition(name, **defn_data)
- cond_name = defn.condition_name()
+ cond_name = defn.condition()
if cond_name is not None:
try:
@@ -205,14 +208,14 @@ class CfnTemplate(CfnTemplateBase):
for arg in super(CfnTemplate, self)._rsrc_defn_args(stack, name, data):
yield arg
- def no_parse(field, path):
- return field
+ parse_cond = functools.partial(self.parse_condition, stack)
yield ('condition',
self._parse_resource_field(self.RES_CONDITION,
- (six.string_types, bool),
+ (six.string_types, bool,
+ function.Function),
'string or boolean',
- name, data, no_parse))
+ name, data, parse_cond))
class HeatTemplate(CfnTemplateBase):
diff --git a/heat/engine/conditions.py b/heat/engine/conditions.py
index 3cc31c66d..cc18fa9b6 100644
--- a/heat/engine/conditions.py
+++ b/heat/engine/conditions.py
@@ -53,6 +53,9 @@ class Conditions(object):
if condition_name is None:
return True
+ if isinstance(condition_name, bool):
+ return condition_name
+
if not (isinstance(condition_name, six.string_types) and
condition_name in self._conditions):
raise ValueError(_('Invalid condition "%s"') % condition_name)
diff --git a/heat/engine/hot/functions.py b/heat/engine/hot/functions.py
index 0b9e8277f..007750638 100644
--- a/heat/engine/hot/functions.py
+++ b/heat/engine/hot/functions.py
@@ -1149,17 +1149,22 @@ class If(function.Macro):
not isinstance(self.args, collections.Sequence) or
isinstance(self.args, six.string_types)):
raise ValueError()
- cd_name, value_if_true, value_if_false = self.args
+ condition, value_if_true, value_if_false = self.args
except ValueError:
msg = _('Arguments to "%s" must be of the form: '
'[condition_name, value_if_true, value_if_false]')
raise ValueError(msg % self.fn_name)
- cd = self._get_condition(cd_name)
+ cond = self.template.parse_condition(self.stack, condition,
+ self.fn_name)
+ cd = self._get_condition(function.resolve(cond))
return parse_func(value_if_true if cd else value_if_false)
- def _get_condition(self, cd_name):
- return self.template.conditions(self.stack).is_enabled(cd_name)
+ def _get_condition(self, cond):
+ if isinstance(cond, bool):
+ return cond
+
+ return self.template.conditions(self.stack).is_enabled(cond)
class ConditionBoolean(function.Function):
diff --git a/heat/engine/hot/template.py b/heat/engine/hot/template.py
index af280a2e8..f6d0e1a8d 100644
--- a/heat/engine/hot/template.py
+++ b/heat/engine/hot/template.py
@@ -241,7 +241,7 @@ class HOTemplate20130523(template_common.CommonTemplate):
raise exception.StackValidationFailed(message=msg)
defn = rsrc_defn.ResourceDefinition(name, **defn_data)
- cond_name = defn.condition_name()
+ cond_name = defn.condition()
if cond_name is not None:
try:
@@ -494,9 +494,7 @@ class HOTemplate20161014(HOTemplate20160408):
yield arg
parse = functools.partial(self.parse, stack)
-
- def no_parse(field, path):
- return field
+ parse_cond = functools.partial(self.parse_condition, stack)
yield ('external_id',
self._parse_resource_field(self.RES_EXTERNAL_ID,
@@ -507,6 +505,7 @@ class HOTemplate20161014(HOTemplate20160408):
yield ('condition',
self._parse_resource_field(self.RES_CONDITION,
- (six.string_types, bool),
- 'string or boolean',
- name, data, no_parse))
+ (six.string_types, bool,
+ function.Function),
+ 'string_or_boolean',
+ name, data, parse_cond))
diff --git a/heat/engine/rsrc_defn.py b/heat/engine/rsrc_defn.py
index 49e780e2e..4efff2fd1 100644
--- a/heat/engine/rsrc_defn.py
+++ b/heat/engine/rsrc_defn.py
@@ -135,8 +135,9 @@ class ResourceDefinitionCore(object):
self._deletion_policy = self.RETAIN
if condition is not None:
- assert isinstance(condition, six.string_types)
- self._hash ^= hash(condition)
+ assert isinstance(condition, (six.string_types, bool,
+ function.Function))
+ self._hash ^= _hash_data(condition)
def freeze(self, **overrides):
"""Return a frozen resource definition, with all functions resolved.
@@ -268,12 +269,12 @@ class ResourceDefinitionCore(object):
"""Return the external resource id."""
return function.resolve(self._external_id)
- def condition_name(self):
+ def condition(self):
"""Return the name of the conditional inclusion rule, if any.
Returns None if the resource is included unconditionally.
"""
- return self._condition
+ return function.resolve(self._condition)
def render_hot(self):
"""Return a HOT snippet for the resource definition."""
diff --git a/heat/engine/template_common.py b/heat/engine/template_common.py
index f421d18f9..fb5557a07 100644
--- a/heat/engine/template_common.py
+++ b/heat/engine/template_common.py
@@ -179,9 +179,12 @@ class CommonTemplate(template.Template):
description = val.get(self.OUTPUT_DESCRIPTION)
if hasattr(self, 'OUTPUT_CONDITION'):
- cond_name = val.get(self.OUTPUT_CONDITION)
+ path = [self.OUTPUTS, key, self.OUTPUT_CONDITION]
+ cond = self.parse_condition(stack,
+ val.get(self.OUTPUT_CONDITION),
+ '.'.join(path))
try:
- enabled = conds.is_enabled(cond_name)
+ enabled = conds.is_enabled(function.resolve(cond))
except ValueError as exc:
path = [self.OUTPUTS, key, self.OUTPUT_CONDITION]
message = six.text_type(exc)
diff --git a/heat/tests/test_hot.py b/heat/tests/test_hot.py
index 6be84fa60..494dfcd26 100644
--- a/heat/tests/test_hot.py
+++ b/heat/tests/test_hot.py
@@ -1180,6 +1180,35 @@ class HOTemplateTest(common.HeatTestCase):
resolved = self.resolve(snippet, tmpl, stack)
self.assertEqual('value_if_false', resolved)
+ def test_if_using_boolean_condition(self):
+ snippet = {'if': [True, 'value_if_true', 'value_if_false']}
+ # when condition is true, if function resolve to value_if_true
+ tmpl = template.Template(hot_newton_tpl_empty)
+ stack = parser.Stack(utils.dummy_context(),
+ 'test_if_using_boolean_condition', tmpl)
+ resolved = self.resolve(snippet, tmpl, stack)
+ self.assertEqual('value_if_true', resolved)
+ # when condition is false, if function resolve to value_if_false
+ snippet = {'if': [False, 'value_if_true', 'value_if_false']}
+ resolved = self.resolve(snippet, tmpl, stack)
+ self.assertEqual('value_if_false', resolved)
+
+ def test_if_using_condition_function(self):
+ tmpl_with_conditions = template_format.parse('''
+heat_template_version: 2016-10-14
+conditions:
+ create_prod: False
+''')
+ snippet = {'if': [{'not': 'create_prod'},
+ 'value_if_true', 'value_if_false']}
+
+ tmpl = template.Template(tmpl_with_conditions)
+ stack = parser.Stack(utils.dummy_context(),
+ 'test_if_using_condition_function', tmpl)
+
+ resolved = self.resolve(snippet, tmpl, stack)
+ self.assertEqual('value_if_true', resolved)
+
def test_if_invalid_args(self):
snippet = {'if': ['create_prod', 'one_value']}
tmpl = template.Template(hot_newton_tpl_empty)
diff --git a/heat/tests/test_template.py b/heat/tests/test_template.py
index 9c91794fa..5c8d12b04 100644
--- a/heat/tests/test_template.py
+++ b/heat/tests/test_template.py
@@ -297,7 +297,7 @@ class TestTemplateConditionParser(common.HeatTestCase):
'outputs': {
'foo': {
'condition': 'prod_env',
- 'value': {'get_attr': ['r1', 'foo']}
+ 'value': 'show me'
}
}
}
@@ -400,6 +400,15 @@ class TestTemplateConditionParser(common.HeatTestCase):
ex = self.assertRaises(ValueError, conds.is_enabled, 111)
self.assertIn('Invalid condition "111"', six.text_type(ex))
+ def test_res_condition_using_boolean(self):
+ tmpl = copy.deepcopy(self.tmpl)
+ # test condition name is boolean
+ stk = stack.Stack(self.ctx, 'test_res_cd_boolean', tmpl)
+
+ conds = tmpl.conditions(stk)
+ self.assertTrue(conds.is_enabled(True))
+ self.assertFalse(conds.is_enabled(False))
+
def test_parse_output_condition_invalid(self):
stk = stack.Stack(self.ctx,
'test_output_invalid_condition',
@@ -441,6 +450,31 @@ class TestTemplateConditionParser(common.HeatTestCase):
self.assertIn('Circular definition for condition "first_cond"',
six.text_type(ex))
+ def test_parse_output_condition_boolean(self):
+ t = copy.deepcopy(self.tmpl.t)
+ t['outputs']['foo']['condition'] = True
+ stk = stack.Stack(self.ctx,
+ 'test_output_cd_boolean',
+ template.Template(t))
+
+ self.assertEqual('show me', stk.outputs['foo'].get_value())
+
+ t = copy.deepcopy(self.tmpl.t)
+ t['outputs']['foo']['condition'] = False
+ stk = stack.Stack(self.ctx,
+ 'test_output_cd_boolean',
+ template.Template(t))
+ self.assertIsNone(stk.outputs['foo'].get_value())
+
+ def test_parse_output_condition_function(self):
+ t = copy.deepcopy(self.tmpl.t)
+ t['outputs']['foo']['condition'] = {'not': 'prod_env'}
+ stk = stack.Stack(self.ctx,
+ 'test_output_cd_function',
+ template.Template(t))
+
+ self.assertEqual('show me', stk.outputs['foo'].get_value())
+
class TestTemplateValidate(common.HeatTestCase):