Merge "Allows condition name using boolean or function"
This commit is contained in:
commit
9df7ee3a09
|
@ -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``
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1130,17 +1130,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):
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1207,6 +1207,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)
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
Loading…
Reference in New Issue