Refactor boolean condition functions

Change-Id: I0c524b058df0266c48ade5f7c477762fa3d7483a
This commit is contained in:
Zane Bitter 2016-09-08 17:20:26 -04:00
parent 8262265292
commit 4090dfe926
3 changed files with 63 additions and 106 deletions

View File

@ -426,27 +426,16 @@ class Not(hot_funcs.Not):
returns false for a condition that evaluates to true.
"""
def _get_condition(self):
try:
if (not self.args or
not isinstance(self.args, collections.Sequence) or
isinstance(self.args, six.string_types)):
raise ValueError()
if len(self.args) != 1:
raise ValueError()
return self.args[0]
except ValueError:
msg = _('Arguments to "%s" must be of the form: '
'[condition]')
raise ValueError(msg % self.fn_name)
def result(self):
resolved_value = function.resolve(self.condition)
if not isinstance(resolved_value, bool):
msg = _('The condition value should be boolean, '
'after resolved the value is: %s')
raise ValueError(msg % resolved_value)
return not resolved_value
def _check_args(self):
msg = _('Arguments to "%s" must be of the form: '
'[condition]') % self.fn_name
if (not self.args or
not isinstance(self.args, collections.Sequence) or
isinstance(self.args, six.string_types)):
raise ValueError(msg)
if len(self.args) != 1:
raise ValueError(msg)
self.condition = self.args[0]
class And(hot_funcs.And):

View File

@ -1162,7 +1162,31 @@ class If(function.Macro):
return self.template.conditions(self.stack).is_enabled(cd_name)
class Not(function.Function):
class ConditionBoolean(function.Function):
"""Abstract parent class of boolean condition functions."""
def __init__(self, stack, fn_name, args):
super(ConditionBoolean, self).__init__(stack, fn_name, args)
self._check_args()
def _check_args(self):
if not (isinstance(self.args, collections.Sequence) and
not isinstance(self.args, six.string_types)):
msg = _('Arguments to "%s" must be a list of conditions')
raise ValueError(msg % self.fn_name)
if not self.args or len(self.args) < 2:
msg = _('The minimum number of condition arguments to "%s" is 2.')
raise ValueError(msg % self.fn_name)
def _get_condition(self, arg):
if isinstance(arg, bool):
return arg
msg = _('The condition value must be a boolean: %s')
raise ValueError(msg % arg)
class Not(ConditionBoolean):
"""A function that acts as a NOT operator on a condition.
Takes the form::
@ -1173,30 +1197,18 @@ class Not(function.Function):
returns false for a condition that evaluates to true.
"""
def __init__(self, stack, fn_name, args):
super(Not, self).__init__(stack, fn_name, args)
self.condition = self._get_condition()
def _get_condition(self):
try:
if not self.args:
raise ValueError()
return self.args
except ValueError:
msg = _('Arguments to "%s" must be of the form: '
'condition')
def _check_args(self):
self.condition = self.args
if self.args is None:
msg = _('Argument to "%s" must be a condition')
raise ValueError(msg % self.fn_name)
def result(self):
resolved_value = function.resolve(self.condition)
if not isinstance(resolved_value, bool):
msg = _('The condition value should be boolean, '
'after resolved the value is: %s')
raise ValueError(msg % resolved_value)
return not resolved_value
cd = function.resolve(self.condition)
return not self._get_condition(cd)
class And(function.Function):
class And(ConditionBoolean):
"""A function that acts as an AND operator on conditions.
Takes the form::
@ -1211,31 +1223,12 @@ class And(function.Function):
of conditions that you can include is 2.
"""
def __init__(self, stack, fn_name, args):
super(And, self).__init__(stack, fn_name, args)
if (not self.args or
not (isinstance(self.args, collections.Sequence) and
not isinstance(self.args, six.string_types)) or
len(self.args) < 2):
msg = _('Arguments to "%s" must be of the form: '
'[{condition_1}, {condition_2}, {...}, {condition_n}], '
'the minimum number of conditions is 2.')
raise ValueError(msg % self.fn_name)
def result(self):
for cd in self.args:
resolved_value = function.resolve(cd)
if not isinstance(resolved_value, bool):
msg = _('The condition value should be boolean, '
'after resolved the value is: %s')
raise ValueError(msg % resolved_value)
if not resolved_value:
return False
return True
return all(self._get_condition(cd)
for cd in function.resolve(self.args))
class Or(function.Function):
class Or(ConditionBoolean):
"""A function that acts as an OR operator on conditions.
Takes the form::
@ -1250,25 +1243,6 @@ class Or(function.Function):
number of conditions that you can include is 2.
"""
def __init__(self, stack, fn_name, args):
super(Or, self).__init__(stack, fn_name, args)
if (not self.args or
not (isinstance(self.args, collections.Sequence) and
not isinstance(self.args, six.string_types)) or
len(self.args) < 2):
msg = _('Arguments to "%s" must be of the form: '
'[{condition_1}, {condition_2}, {...}, {condition_n}], '
'the minimum number of conditions is 2.')
raise ValueError(msg % self.fn_name)
def result(self):
for cd in self.args:
resolved_value = function.resolve(cd)
if not isinstance(resolved_value, bool):
msg = _('The condition value should be boolean, '
'after resolved the value is: %s')
raise ValueError(msg % resolved_value)
if resolved_value:
return True
return False
return any(self._get_condition(cd)
for cd in function.resolve(self.args))

View File

@ -962,22 +962,20 @@ class TemplateTest(common.HeatTestCase):
exc = self.assertRaises(ValueError,
self.resolve_condition, snippet, tmpl)
error_msg = ('The condition value should be boolean, '
'after resolved the value is: invalid_arg')
error_msg = ('The condition value must be a boolean: '
'invalid_arg')
self.assertIn(error_msg, six.text_type(exc))
# test invalid type
snippet = {'Fn::Not': 'invalid'}
exc = self.assertRaises(exception.StackValidationFailed,
self.resolve_condition, snippet, tmpl)
error_msg = ('.Fn::Not: Arguments to "Fn::Not" must be '
'of the form: [condition]')
error_msg = 'Arguments to "Fn::Not" must be '
self.assertIn(error_msg, six.text_type(exc))
snippet = {'Fn::Not': ['cd1', 'cd2']}
exc = self.assertRaises(exception.StackValidationFailed,
self.resolve_condition, snippet, tmpl)
error_msg = ('.Fn::Not: Arguments to "Fn::Not" must be '
'of the form: [condition]')
error_msg = 'Arguments to "Fn::Not" must be '
self.assertIn(error_msg, six.text_type(exc))
def test_and(self):
@ -1024,27 +1022,25 @@ class TemplateTest(common.HeatTestCase):
def test_and_invalid_args(self):
tmpl = template.Template(aws_empty_template)
error_msg = ('The minimum number of condition arguments to "Fn::And" '
'is 2.')
snippet = {'Fn::And': ['invalid_arg']}
exc = self.assertRaises(exception.StackValidationFailed,
self.resolve_condition, snippet, tmpl)
error_msg = ('.Fn::And: Arguments to "Fn::And" must be '
'of the form: [{condition_1}, {condition_2}, {...}, '
'{condition_n}]')
self.assertIn(error_msg, six.text_type(exc))
error_msg = 'Arguments to "Fn::And" must be'
# test invalid type
snippet = {'Fn::And': 'invalid'}
exc = self.assertRaises(exception.StackValidationFailed,
self.resolve_condition, snippet, tmpl)
self.assertIn(error_msg, six.text_type(exc))
snippet = {'Fn::And': ['cd1', True]}
exc = self.assertRaises(ValueError,
self.resolve_condition, snippet, tmpl)
error_msg = ('The condition value should be boolean, '
'after resolved the value is: cd1')
error_msg = ('The condition value must be a boolean: '
'cd1')
self.assertIn(error_msg, six.text_type(exc))
def test_or(self):
@ -1087,27 +1083,25 @@ class TemplateTest(common.HeatTestCase):
def test_or_invalid_args(self):
tmpl = template.Template(aws_empty_template)
error_msg = ('The minimum number of condition arguments to "Fn::Or" '
'is 2.')
snippet = {'Fn::Or': ['invalid_arg']}
exc = self.assertRaises(exception.StackValidationFailed,
self.resolve_condition, snippet, tmpl)
error_msg = ('.Fn::Or: Arguments to "Fn::Or" must be '
'of the form: [{condition_1}, {condition_2}, {...}, '
'{condition_n}]')
self.assertIn(error_msg, six.text_type(exc))
error_msg = 'Arguments to "Fn::Or" must be'
# test invalid type
snippet = {'Fn::Or': 'invalid'}
exc = self.assertRaises(exception.StackValidationFailed,
self.resolve_condition, snippet, tmpl)
self.assertIn(error_msg, six.text_type(exc))
snippet = {'Fn::Or': ['invalid_cd', True]}
exc = self.assertRaises(ValueError,
self.resolve_condition, snippet, tmpl)
error_msg = ('The condition value should be boolean, '
'after resolved the value is: invalid_cd')
error_msg = ('The condition value must be a boolean: '
'invalid_cd')
self.assertIn(error_msg, six.text_type(exc))
def test_join(self):