Merge "Improve StackValidationFailed response in properties"
This commit is contained in:
commit
bc60a1543b
@ -83,6 +83,9 @@ class CfnTemplate(template.Template):
|
||||
return dict((name, parameters.Schema.from_dict(name, schema))
|
||||
for name, schema in six.iteritems(params))
|
||||
|
||||
def get_section_name(self, section):
|
||||
return section
|
||||
|
||||
def parameters(self, stack_identifier, user_params, param_defaults=None):
|
||||
return parameters.Parameters(stack_identifier, self,
|
||||
user_params=user_params,
|
||||
|
@ -56,6 +56,12 @@ class HOTemplate20130523(template.Template):
|
||||
cfn_template.CfnTemplate.RESOURCES: RESOURCES,
|
||||
cfn_template.CfnTemplate.OUTPUTS: OUTPUTS}
|
||||
|
||||
_RESOURCE_HOT_TO_CFN_ATTRS = {'type': 'Type',
|
||||
'properties': 'Properties',
|
||||
'metadata': 'Metadata',
|
||||
'depends_on': 'DependsOn',
|
||||
'deletion_policy': 'DeletionPolicy',
|
||||
'update_policy': 'UpdatePolicy'}
|
||||
functions = {
|
||||
'Fn::GetAZs': cfn_funcs.GetAZs,
|
||||
'get_param': hot_funcs.GetParam,
|
||||
@ -157,15 +163,14 @@ class HOTemplate20130523(template.Template):
|
||||
|
||||
def _translate_resources(self, resources):
|
||||
"""Get the resources of the template translated into CFN format."""
|
||||
HOT_TO_CFN_ATTRS = {'type': 'Type',
|
||||
'properties': 'Properties',
|
||||
'metadata': 'Metadata',
|
||||
'depends_on': 'DependsOn',
|
||||
'deletion_policy': 'DeletionPolicy',
|
||||
'update_policy': 'UpdatePolicy'}
|
||||
|
||||
return self._translate_section('resources', 'type', resources,
|
||||
HOT_TO_CFN_ATTRS)
|
||||
self._RESOURCE_HOT_TO_CFN_ATTRS)
|
||||
|
||||
def get_section_name(self, section):
|
||||
cfn_to_hot_attrs = dict(zip(self._RESOURCE_HOT_TO_CFN_ATTRS.values(),
|
||||
self._RESOURCE_HOT_TO_CFN_ATTRS.keys()))
|
||||
return cfn_to_hot_attrs.get(section, section)
|
||||
|
||||
def _translate_outputs(self, outputs):
|
||||
"""Get the outputs of the template translated into CFN format."""
|
||||
|
@ -267,7 +267,6 @@ class Property(object):
|
||||
keys = list(self.schema.schema)
|
||||
schemata = dict((k, self.schema.schema[k]) for k in keys)
|
||||
properties = Properties(schemata, dict(child_values),
|
||||
parent_name=self.name,
|
||||
context=self.context)
|
||||
if validate:
|
||||
properties.validate()
|
||||
@ -343,15 +342,16 @@ class Property(object):
|
||||
class Properties(collections.Mapping):
|
||||
|
||||
def __init__(self, schema, data, resolver=lambda d: d, parent_name=None,
|
||||
context=None):
|
||||
context=None, section=None):
|
||||
self.props = dict((k, Property(s, k, context))
|
||||
for k, s in schema.items())
|
||||
self.resolve = resolver
|
||||
self.data = data
|
||||
if parent_name is None:
|
||||
self.error_prefix = ''
|
||||
else:
|
||||
self.error_prefix = '%s: ' % parent_name
|
||||
self.error_prefix = []
|
||||
if parent_name is not None:
|
||||
self.error_prefix.append(parent_name)
|
||||
if section is not None:
|
||||
self.error_prefix.append(section)
|
||||
self.context = context
|
||||
|
||||
@staticmethod
|
||||
@ -369,38 +369,58 @@ class Properties(collections.Mapping):
|
||||
return {}
|
||||
|
||||
def validate(self, with_value=True):
|
||||
for (key, prop) in self.props.items():
|
||||
# check that update_allowed and immutable
|
||||
# do not contradict each other
|
||||
if prop.update_allowed() and prop.immutable():
|
||||
msg = _("Property %(prop)s: %(ua)s and %(im)s "
|
||||
"cannot both be True") % {
|
||||
'prop': key,
|
||||
'ua': prop.schema.UPDATE_ALLOWED,
|
||||
'im': prop.schema.IMMUTABLE}
|
||||
raise exception.InvalidSchemaError(message=msg)
|
||||
|
||||
if with_value:
|
||||
try:
|
||||
self._get_property_value(key, validate=True)
|
||||
except ValueError as e:
|
||||
msg = _("Property error : %s") % e
|
||||
try:
|
||||
for key in self.data:
|
||||
if key not in self.props:
|
||||
msg = _("Unknown Property %s") % key
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
# are there unimplemented Properties
|
||||
if not prop.implemented() and key in self.data:
|
||||
msg = _("Property %s not implemented yet") % key
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
for (key, prop) in self.props.items():
|
||||
# check that update_allowed and immutable
|
||||
# do not contradict each other
|
||||
if prop.update_allowed() and prop.immutable():
|
||||
msg = _("Property %(prop)s: %(ua)s and %(im)s "
|
||||
"cannot both be True") % {
|
||||
'prop': key,
|
||||
'ua': prop.schema.UPDATE_ALLOWED,
|
||||
'im': prop.schema.IMMUTABLE}
|
||||
raise exception.InvalidSchemaError(message=msg)
|
||||
|
||||
for key in self.data:
|
||||
if key not in self.props:
|
||||
msg = _("Unknown Property %s") % key
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
if with_value:
|
||||
try:
|
||||
self._get_property_value(key, validate=True)
|
||||
except exception.StackValidationFailed as ex:
|
||||
path = [key]
|
||||
path.extend(ex.path)
|
||||
raise exception.StackValidationFailed(
|
||||
path=path, message=ex.error_message)
|
||||
except ValueError as e:
|
||||
if prop.required() and key not in self.data:
|
||||
path = []
|
||||
else:
|
||||
path = [key]
|
||||
raise exception.StackValidationFailed(
|
||||
path=path, message=six.text_type(e))
|
||||
|
||||
# are there unimplemented Properties
|
||||
if not prop.implemented() and key in self.data:
|
||||
msg = _("Property %s not implemented yet") % key
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
except exception.StackValidationFailed as ex:
|
||||
# NOTE(prazumovsky): should reraise exception for adding specific
|
||||
# error name and error_prefix to path for correct error message
|
||||
# building.
|
||||
path = self.error_prefix
|
||||
path.extend(ex.path)
|
||||
raise exception.StackValidationFailed(
|
||||
error=ex.error or 'Property error',
|
||||
path=path,
|
||||
message=ex.error_message
|
||||
)
|
||||
|
||||
def _get_property_value(self, key, validate=False):
|
||||
if key not in self:
|
||||
raise KeyError(_('%(prefix)sInvalid Property %(key)s') %
|
||||
{'prefix': self.error_prefix, 'key': key})
|
||||
raise KeyError(_('Invalid Property %s') % key)
|
||||
|
||||
prop = self.props[key]
|
||||
|
||||
@ -414,16 +434,20 @@ class Properties(collections.Mapping):
|
||||
|
||||
value = self.resolve(unresolved_value)
|
||||
return prop.get_value(value, validate)
|
||||
# Children can raise StackValidationFailed with unique path which
|
||||
# is necessary for further use in StackValidationFailed exception.
|
||||
# So we need to handle this exception in this method.
|
||||
except exception.StackValidationFailed as e:
|
||||
raise exception.StackValidationFailed(path=e.path,
|
||||
message=e.error_message)
|
||||
# the resolver function could raise any number of exceptions,
|
||||
# so handle this generically
|
||||
except Exception as e:
|
||||
raise ValueError('%s%s %s' % (self.error_prefix, key,
|
||||
six.text_type(e)))
|
||||
raise ValueError(six.text_type(e))
|
||||
elif prop.has_default():
|
||||
return prop.get_value(None, validate)
|
||||
elif prop.required():
|
||||
raise ValueError(_('%(prefix)sProperty %(key)s not assigned') %
|
||||
{'prefix': self.error_prefix, 'key': key})
|
||||
raise ValueError(_('Property %s not assigned') % key)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self._get_property_value(key)
|
||||
|
@ -874,7 +874,18 @@ class Resource(object):
|
||||
|
||||
function.validate(self.t)
|
||||
self.validate_deletion_policy(self.t.deletion_policy())
|
||||
return self.properties.validate(with_value=self.stack.strict_validate)
|
||||
try:
|
||||
validate = self.properties.validate(
|
||||
with_value=self.stack.strict_validate)
|
||||
except exception.StackValidationFailed as ex:
|
||||
path = [self.stack.t.RESOURCES, ex.path[0],
|
||||
self.stack.t.get_section_name(ex.path[1])]
|
||||
path.extend(ex.path[2:])
|
||||
raise exception.StackValidationFailed(
|
||||
error=ex.error,
|
||||
path=path,
|
||||
message=ex.error_message)
|
||||
return validate
|
||||
|
||||
@classmethod
|
||||
def validate_deletion_policy(cls, policy):
|
||||
|
@ -183,7 +183,8 @@ class ResourceDefinitionCore(object):
|
||||
require a context to validate constraints.
|
||||
"""
|
||||
return properties.Properties(schema, self._properties or {},
|
||||
function.resolve, self.name, context)
|
||||
function.resolve, self.name, context,
|
||||
section=PROPERTIES)
|
||||
|
||||
def deletion_policy(self):
|
||||
"""
|
||||
@ -201,7 +202,8 @@ class ResourceDefinitionCore(object):
|
||||
require a context to validate constraints.
|
||||
"""
|
||||
return properties.Properties(schema, self._update_policy or {},
|
||||
function.resolve, self.name, context)
|
||||
function.resolve, self.name, context,
|
||||
section=UPDATE_POLICY)
|
||||
|
||||
def metadata(self):
|
||||
"""
|
||||
|
@ -816,7 +816,7 @@ class EngineService(service.Service):
|
||||
|
||||
env = environment.Environment(params)
|
||||
|
||||
for res in tmpl_resources.values():
|
||||
for name, res in six.iteritems(tmpl_resources):
|
||||
ResourceClass = env.get_class(res['Type'])
|
||||
if ResourceClass == resources.template_resource.TemplateResource:
|
||||
# we can't validate a TemplateResource unless we instantiate
|
||||
@ -824,9 +824,12 @@ class EngineService(service.Service):
|
||||
# parameters into properties_schema.
|
||||
continue
|
||||
|
||||
props = properties.Properties(ResourceClass.properties_schema,
|
||||
res.get('Properties', {}),
|
||||
context=cnxt)
|
||||
props = properties.Properties(
|
||||
ResourceClass.properties_schema,
|
||||
res.get('Properties', {}),
|
||||
parent_name=six.text_type(name),
|
||||
context=cnxt,
|
||||
section='Properties')
|
||||
deletion_policy = res.get('DeletionPolicy', 'Delete')
|
||||
try:
|
||||
ResourceClass.validate_deletion_policy(deletion_policy)
|
||||
|
@ -158,6 +158,11 @@ class Template(collections.Mapping):
|
||||
'''Return a dict of parameters.Schema objects for the parameters.'''
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_section_name(self, section):
|
||||
"""Return a correct section name."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def parameters(self, stack_identifier, user_params, param_defaults=None):
|
||||
'''Return a parameters.Parameters object for the stack.'''
|
||||
|
@ -133,8 +133,10 @@ class LaunchConfigurationTest(common.HeatTestCase):
|
||||
|
||||
self.patchobject(nova.NovaClientPlugin, 'get_server',
|
||||
side_effect=exception.ServerNotFound(server='5678'))
|
||||
msg = ("Property error : LaunchConfig: InstanceId Error validating "
|
||||
"value '5678': The server (5678) could not be found.")
|
||||
msg = ("Property error : "
|
||||
"Resources.LaunchConfig.Properties.InstanceId: "
|
||||
"Error validating value '5678': The server (5678) "
|
||||
"could not be found.")
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
rsrc.validate)
|
||||
self.assertIn(msg, six.text_type(exc))
|
||||
@ -193,9 +195,10 @@ class LaunchConfigurationTest(common.HeatTestCase):
|
||||
self.validate_launch_config, t,
|
||||
stack, 'LaunchConfig')
|
||||
|
||||
excepted_error = ('Property error : LaunchConfig: BlockDeviceMappings '
|
||||
'Property error : BlockDeviceMappings: 0 Property '
|
||||
'error : 0: Property DeviceName not assigned')
|
||||
excepted_error = (
|
||||
'Property error : '
|
||||
'Resources.LaunchConfig.Properties.BlockDeviceMappings[0]: '
|
||||
'Property DeviceName not assigned')
|
||||
self.assertIn(excepted_error, six.text_type(e))
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
@ -220,8 +220,10 @@ class TestAutoScalingGroupValidation(common.HeatTestCase):
|
||||
rsrc = stack['WebServerGroup']
|
||||
self._stub_nova_server_get(not_found=True)
|
||||
self.m.ReplayAll()
|
||||
msg = ("Property error : WebServerGroup: InstanceId Error validating "
|
||||
"value '5678': The server (5678) could not be found")
|
||||
msg = ("Property error : "
|
||||
"Resources.WebServerGroup.Properties.InstanceId: "
|
||||
"Error validating value '5678': The server (5678) could "
|
||||
"not be found.")
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
rsrc.validate)
|
||||
self.assertIn(msg, six.text_type(exc))
|
||||
|
@ -203,7 +203,9 @@ class VolumeTest(vt_base.BaseVolumeTest):
|
||||
|
||||
ex = self.assertRaises(exception.StackValidationFailed,
|
||||
self.create_volume, self.t, stack, 'DataVolume')
|
||||
self.assertIn('Tags Property error', six.text_type(ex))
|
||||
self.assertEqual("Property error : "
|
||||
"Resources.DataVolume.Properties.Tags[0]: "
|
||||
"Unknown Property Foo", six.text_type(ex))
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
@ -664,8 +666,8 @@ class VolumeTest(vt_base.BaseVolumeTest):
|
||||
self.create_volume,
|
||||
self.t, stack, 'DataVolume')
|
||||
self.assertEqual(
|
||||
"Property error : DataVolume: Size 0 is out of "
|
||||
"range (min: 1, max: None)", six.text_type(error))
|
||||
"Property error : Resources.DataVolume.Properties.Size: "
|
||||
"0 is out of range (min: 1, max: None)", six.text_type(error))
|
||||
|
||||
def test_volume_attachment_updates_not_supported(self):
|
||||
self.m.StubOutWithMock(nova.NovaClientPlugin, 'get_server')
|
||||
|
@ -103,8 +103,8 @@ class CinderVolumeTest(vt_base.BaseVolumeTest):
|
||||
self.create_volume,
|
||||
self.t, stack, 'volume')
|
||||
self.assertEqual(
|
||||
"Property error : volume: size 0 is out of "
|
||||
"range (min: 1, max: None)", six.text_type(error))
|
||||
"Property error : resources.volume.properties.size: "
|
||||
"0 is out of range (min: 1, max: None)", six.text_type(error))
|
||||
|
||||
def test_cinder_create(self):
|
||||
fv = vt_base.FakeVolume('creating')
|
||||
|
@ -377,8 +377,8 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
error = self.assertRaises(exception.StackValidationFailed,
|
||||
rsrc.validate)
|
||||
self.assertEqual(
|
||||
"Property error : MEMAlarmHigh: %s Value '60a' is not an "
|
||||
"integer" % p, six.text_type(error))
|
||||
"Property error : Resources.MEMAlarmHigh.Properties.%s: "
|
||||
"Value '60a' is not an integer" % p, six.text_type(error))
|
||||
|
||||
def test_mem_alarm_high_not_integer_parameters(self):
|
||||
snippet = template_format.parse(not_string_alarm_template)
|
||||
@ -392,8 +392,9 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
error = self.assertRaises(exception.StackValidationFailed,
|
||||
rsrc.validate)
|
||||
self.assertEqual(
|
||||
"Property error : MEMAlarmHigh: %s int() argument must be "
|
||||
"a string or a number, not 'list'" % p, six.text_type(error))
|
||||
"Property error : Resources.MEMAlarmHigh.Properties.%s: "
|
||||
"int() argument must be a string or a number, not "
|
||||
"'list'" % p, six.text_type(error))
|
||||
|
||||
def test_mem_alarm_high_check_not_required_parameters(self):
|
||||
snippet = template_format.parse(not_string_alarm_template)
|
||||
@ -406,7 +407,8 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
error = self.assertRaises(exception.StackValidationFailed,
|
||||
rsrc.validate)
|
||||
self.assertEqual(
|
||||
"Property error : MEMAlarmHigh: Property meter_name not assigned",
|
||||
"Property error : Resources.MEMAlarmHigh.Properties: "
|
||||
"Property meter_name not assigned",
|
||||
six.text_type(error))
|
||||
|
||||
for p in ('period', 'evaluation_periods', 'statistic',
|
||||
@ -521,8 +523,9 @@ class CombinationAlarmTest(common.HeatTestCase):
|
||||
error = self.assertRaises(exception.StackValidationFailed,
|
||||
rsrc.validate)
|
||||
self.assertEqual(
|
||||
"Property error : CombinAlarm: alarm_ids length (0) is out of "
|
||||
"range (min: 1, max: None)", six.text_type(error))
|
||||
"Property error : Resources.CombinAlarm.Properties.alarm_ids: "
|
||||
"length (0) is out of range (min: 1, max: None)",
|
||||
six.text_type(error))
|
||||
|
||||
def test_update(self):
|
||||
rsrc = self.create_alarm()
|
||||
|
@ -99,7 +99,8 @@ class GlanceImageTest(common.HeatTestCase):
|
||||
)
|
||||
image = stack['image']
|
||||
image.t['Properties']['min_disk'] = -1
|
||||
error_msg = 'min_disk -1 is out of range (min: 0, max: None)'
|
||||
error_msg = ('Property error : resources.image.properties.min_disk: '
|
||||
'-1 is out of range (min: 0, max: None)')
|
||||
self._test_validate(image, error_msg)
|
||||
|
||||
def test_invalid_min_ram(self):
|
||||
@ -111,7 +112,8 @@ class GlanceImageTest(common.HeatTestCase):
|
||||
)
|
||||
image = stack['image']
|
||||
image.t['Properties']['min_ram'] = -1
|
||||
error_msg = 'min_ram -1 is out of range (min: 0, max: None)'
|
||||
error_msg = ('Property error : resources.image.properties.min_ram: '
|
||||
'-1 is out of range (min: 0, max: None)')
|
||||
self._test_validate(image, error_msg)
|
||||
|
||||
def test_miss_disk_format(self):
|
||||
@ -135,7 +137,9 @@ class GlanceImageTest(common.HeatTestCase):
|
||||
)
|
||||
image = stack['image']
|
||||
image.t['Properties']['disk_format'] = 'incorrect_format'
|
||||
error_msg = ('disk_format "incorrect_format" is not an allowed value '
|
||||
error_msg = ('Property error : '
|
||||
'resources.image.properties.disk_format: '
|
||||
'"incorrect_format" is not an allowed value '
|
||||
'[ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, iso]')
|
||||
self._test_validate(image, error_msg)
|
||||
|
||||
@ -160,8 +164,10 @@ class GlanceImageTest(common.HeatTestCase):
|
||||
)
|
||||
image = stack['image']
|
||||
image.t['Properties']['container_format'] = 'incorrect_format'
|
||||
error_msg = ('container_format "incorrect_format" is not an '
|
||||
'allowed value [ami, ari, aki, bare, ova, ovf]')
|
||||
error_msg = ('Property error : '
|
||||
'resources.image.properties.container_format: '
|
||||
'"incorrect_format" is not an allowed value '
|
||||
'[ami, ari, aki, bare, ova, ovf]')
|
||||
self._test_validate(image, error_msg)
|
||||
|
||||
def test_miss_location(self):
|
||||
|
@ -227,8 +227,9 @@ class InstancesTest(common.HeatTestCase):
|
||||
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
instance.validate)
|
||||
self.assertIn("VolumeId Error validating value '1234': "
|
||||
"The Volume (1234) could not be found.",
|
||||
self.assertIn("WebServer.Properties.Volumes[0].VolumeId: "
|
||||
"Error validating value '1234': The Volume "
|
||||
"(1234) could not be found.",
|
||||
six.text_type(exc))
|
||||
|
||||
self.m.VerifyAll()
|
||||
@ -322,9 +323,10 @@ class InstancesTest(common.HeatTestCase):
|
||||
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
instance.validate)
|
||||
excepted_error = ('Property error : WebServer: BlockDeviceMappings '
|
||||
'Property error : BlockDeviceMappings: 0 Property '
|
||||
'error : 0: Property DeviceName not assigned')
|
||||
excepted_error = (
|
||||
'Property error : '
|
||||
'Resources.WebServer.Properties.BlockDeviceMappings[0]: '
|
||||
'Property DeviceName not assigned')
|
||||
self.assertIn(excepted_error, six.text_type(exc))
|
||||
|
||||
self.m.VerifyAll()
|
||||
@ -382,9 +384,9 @@ class InstancesTest(common.HeatTestCase):
|
||||
create = scheduler.TaskRunner(instance.create)
|
||||
error = self.assertRaises(exception.ResourceFailure, create)
|
||||
self.assertEqual(
|
||||
"StackValidationFailed: Property error : WebServer: "
|
||||
"ImageId Error validating value 'Slackware': "
|
||||
"The Image (Slackware) could not be found.",
|
||||
"StackValidationFailed: Property error : "
|
||||
"WebServer.Properties.ImageId: Error validating value "
|
||||
"'Slackware': The Image (Slackware) could not be found.",
|
||||
six.text_type(error))
|
||||
|
||||
self.m.VerifyAll()
|
||||
@ -409,9 +411,9 @@ class InstancesTest(common.HeatTestCase):
|
||||
create = scheduler.TaskRunner(instance.create)
|
||||
error = self.assertRaises(exception.ResourceFailure, create)
|
||||
self.assertEqual(
|
||||
'StackValidationFailed: Property error : WebServer: '
|
||||
'ImageId Multiple physical resources were '
|
||||
'found with name (CentOS 5.2).',
|
||||
'StackValidationFailed: Property error : '
|
||||
'WebServer.Properties.ImageId: Multiple physical '
|
||||
'resources were found with name (CentOS 5.2).',
|
||||
six.text_type(error))
|
||||
|
||||
self.m.VerifyAll()
|
||||
@ -433,8 +435,8 @@ class InstancesTest(common.HeatTestCase):
|
||||
create = scheduler.TaskRunner(instance.create)
|
||||
error = self.assertRaises(exception.ResourceFailure, create)
|
||||
self.assertEqual(
|
||||
'StackValidationFailed: Property error : WebServer: '
|
||||
'ImageId 404 (HTTP 404)',
|
||||
'StackValidationFailed: Property error : '
|
||||
'WebServer.Properties.ImageId: 404 (HTTP 404)',
|
||||
six.text_type(error))
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
@ -103,11 +103,11 @@ class NovaKeyPairTest(common.HeatTestCase):
|
||||
definition = stack.t.resource_definitions(stack)['kp']
|
||||
kp_res = nova_keypair.KeyPair('kp', definition, stack)
|
||||
self.m.ReplayAll()
|
||||
create = scheduler.TaskRunner(kp_res.create)
|
||||
error = self.assertRaises(exception.ResourceFailure, create)
|
||||
error = self.assertRaises(exception.StackValidationFailed,
|
||||
kp_res.validate)
|
||||
self.assertIn("Property error", six.text_type(error))
|
||||
self.assertIn("name length (0) is out of range (min: 1, max: 255)",
|
||||
six.text_type(error))
|
||||
self.assertIn("kp.properties.name: length (0) is out of "
|
||||
"range (min: 1, max: 255)", six.text_type(error))
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_create_key_excess_name_length(self):
|
||||
@ -119,11 +119,11 @@ class NovaKeyPairTest(common.HeatTestCase):
|
||||
definition = stack.t.resource_definitions(stack)['kp']
|
||||
kp_res = nova_keypair.KeyPair('kp', definition, stack)
|
||||
self.m.ReplayAll()
|
||||
create = scheduler.TaskRunner(kp_res.create)
|
||||
error = self.assertRaises(exception.ResourceFailure, create)
|
||||
error = self.assertRaises(exception.StackValidationFailed,
|
||||
kp_res.validate)
|
||||
self.assertIn("Property error", six.text_type(error))
|
||||
self.assertIn("name length (256) is out of range (min: 1, max: 255)",
|
||||
six.text_type(error))
|
||||
self.assertIn("kp.properties.name: length (256) is out of "
|
||||
"range (min: 1, max: 255)", six.text_type(error))
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_delete_key(self):
|
||||
|
@ -882,8 +882,8 @@ class PropertyTest(common.HeatTestCase):
|
||||
p = properties.Property({'Type': 'Map', 'Schema': map_schema})
|
||||
ex = self.assertRaises(exception.StackValidationFailed,
|
||||
p.get_value, {'valid': 'fish'}, True)
|
||||
self.assertEqual('Property error : valid "fish" is not '
|
||||
'a valid boolean', six.text_type(ex))
|
||||
self.assertEqual('Property error : valid: "fish" is not a '
|
||||
'valid boolean', six.text_type(ex))
|
||||
|
||||
def test_map_schema_missing_data(self):
|
||||
map_schema = {'valid': {'Type': 'Boolean'}}
|
||||
@ -914,8 +914,8 @@ class PropertyTest(common.HeatTestCase):
|
||||
ex = self.assertRaises(exception.StackValidationFailed,
|
||||
p.get_value,
|
||||
[{'valid': 'True'}, {'valid': 'fish'}], True)
|
||||
self.assertEqual('Property error : 1 Property error : 1: valid '
|
||||
'"fish" is not a valid boolean', six.text_type(ex))
|
||||
self.assertEqual('Property error : [1].valid: "fish" is not '
|
||||
'a valid boolean', six.text_type(ex))
|
||||
|
||||
def test_list_schema_int_good(self):
|
||||
list_schema = {'Type': 'Integer'}
|
||||
@ -927,7 +927,7 @@ class PropertyTest(common.HeatTestCase):
|
||||
p = properties.Property({'Type': 'List', 'Schema': list_schema})
|
||||
ex = self.assertRaises(exception.StackValidationFailed,
|
||||
p.get_value, [42, 'fish'], True)
|
||||
self.assertEqual("Property error : 1 Value 'fish' is not "
|
||||
self.assertEqual("Property error : [1]: Value 'fish' is not "
|
||||
"an integer", six.text_type(ex))
|
||||
|
||||
|
||||
@ -1056,16 +1056,6 @@ class PropertiesTest(common.HeatTestCase):
|
||||
props = properties.Properties(schema, {'foo': None})
|
||||
self.assertEqual(['one', 'two'], props['foo'])
|
||||
|
||||
def test_bad_resolver(self):
|
||||
schema = {'foo': {'Type': 'String', 'Default': 'bar'}}
|
||||
|
||||
def bad_resolver(prop):
|
||||
raise Exception('resolution failed!')
|
||||
|
||||
props = properties.Properties(schema, {'foo': 'baz'}, bad_resolver)
|
||||
err = self.assertRaises(ValueError, props.get, 'foo')
|
||||
self.assertEqual('foo resolution failed!', six.text_type(err))
|
||||
|
||||
def test_resolve_returns_none(self):
|
||||
schema = {'foo': {'Type': 'String', "MinLength": "5"}}
|
||||
|
||||
@ -1543,14 +1533,14 @@ class PropertiesValidationTest(common.HeatTestCase):
|
||||
schema = {'foo': {'Type': 'String'}}
|
||||
props = properties.Properties(schema, {'foo': ['foo', 'bar']})
|
||||
ex = self.assertRaises(exception.StackValidationFailed, props.validate)
|
||||
self.assertEqual('Property error : foo Value must be a string',
|
||||
self.assertEqual('Property error : foo: Value must be a string',
|
||||
six.text_type(ex))
|
||||
|
||||
def test_dict_instead_string(self):
|
||||
schema = {'foo': {'Type': 'String'}}
|
||||
props = properties.Properties(schema, {'foo': {'foo': 'bar'}})
|
||||
ex = self.assertRaises(exception.StackValidationFailed, props.validate)
|
||||
self.assertEqual('Property error : foo Value must be a string',
|
||||
self.assertEqual('Property error : foo: Value must be a string',
|
||||
six.text_type(ex))
|
||||
|
||||
def test_none_string(self):
|
||||
@ -1717,8 +1707,8 @@ class PropertiesValidationTest(common.HeatTestCase):
|
||||
props = properties.Properties(schema, invalid_data)
|
||||
ex = self.assertRaises(exception.StackValidationFailed,
|
||||
props.validate)
|
||||
self.assertEqual('Property error : foo Property error : foo: 0 '
|
||||
'Unknown Property bar', six.text_type(ex))
|
||||
self.assertEqual('Property error : foo[0]: Unknown Property bar',
|
||||
six.text_type(ex))
|
||||
|
||||
def test_nested_properties_schema_invalid_property_in_map(self):
|
||||
child_schema = {'Key': {'Type': 'String',
|
||||
@ -1736,8 +1726,8 @@ class PropertiesValidationTest(common.HeatTestCase):
|
||||
props = properties.Properties(schema, invalid_data)
|
||||
ex = self.assertRaises(exception.StackValidationFailed,
|
||||
props.validate)
|
||||
self.assertEqual('Property error : foo Property error : foo: boo '
|
||||
'Unknown Property bar', six.text_type(ex))
|
||||
self.assertEqual('Property error : foo.boo: Unknown Property bar',
|
||||
six.text_type(ex))
|
||||
|
||||
def test_more_nested_properties_schema_invalid_property_in_list(self):
|
||||
nested_child_schema = {'Key': {'Type': 'String',
|
||||
@ -1754,8 +1744,7 @@ class PropertiesValidationTest(common.HeatTestCase):
|
||||
props = properties.Properties(schema, invalid_data)
|
||||
ex = self.assertRaises(exception.StackValidationFailed,
|
||||
props.validate)
|
||||
self.assertEqual('Property error : foo Property error : foo: 0 '
|
||||
'Property error : 0: doo Unknown Property bar',
|
||||
self.assertEqual('Property error : foo[0].doo: Unknown Property bar',
|
||||
six.text_type(ex))
|
||||
|
||||
def test_more_nested_properties_schema_invalid_property_in_map(self):
|
||||
@ -1773,8 +1762,7 @@ class PropertiesValidationTest(common.HeatTestCase):
|
||||
props = properties.Properties(schema, invalid_data)
|
||||
ex = self.assertRaises(exception.StackValidationFailed,
|
||||
props.validate)
|
||||
self.assertEqual('Property error : foo Property error : foo: boo '
|
||||
'Property error : boo: doo Unknown Property bar',
|
||||
self.assertEqual('Property error : foo.boo.doo: Unknown Property bar',
|
||||
six.text_type(ex))
|
||||
|
||||
def test_schema_to_template_empty_schema(self):
|
||||
|
@ -223,7 +223,7 @@ Resources:
|
||||
'''
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
self.create_stack, template_random_string)
|
||||
self.assertIn('length 513 is out of range (min: 1, max: 512)',
|
||||
self.assertIn('513 is out of range (min: 1, max: 512)',
|
||||
six.text_type(exc))
|
||||
|
||||
|
||||
|
@ -547,7 +547,8 @@ class ResourceTest(common.HeatTestCase):
|
||||
tmpl = rsrc_defn.ResourceDefinition(rname, 'Foo', {})
|
||||
res = generic_rsrc.ResourceWithRequiredProps(rname, tmpl, self.stack)
|
||||
|
||||
estr = 'Property error : test_resource: Property Foo not assigned'
|
||||
estr = ('Property error : test_resource.Properties: '
|
||||
'Property Foo not assigned')
|
||||
create = scheduler.TaskRunner(res.create)
|
||||
err = self.assertRaises(exception.ResourceFailure, create)
|
||||
self.assertIn(estr, six.text_type(err))
|
||||
@ -559,7 +560,8 @@ class ResourceTest(common.HeatTestCase):
|
||||
{'Food': 'abc'})
|
||||
res = generic_rsrc.ResourceWithProps(rname, tmpl, self.stack)
|
||||
|
||||
estr = 'StackValidationFailed: Unknown Property Food'
|
||||
estr = ('StackValidationFailed: Property error : '
|
||||
'test_resource.Properties: Unknown Property Food')
|
||||
create = scheduler.TaskRunner(res.create)
|
||||
err = self.assertRaises(exception.ResourceFailure, create)
|
||||
self.assertIn(estr, six.text_type(err))
|
||||
@ -1506,8 +1508,9 @@ class ResourceDependenciesTest(common.HeatTestCase):
|
||||
stack = parser.Stack(utils.dummy_context(), 'test', tmpl)
|
||||
ex = self.assertRaises(exception.StackValidationFailed,
|
||||
stack.validate)
|
||||
expected = "FooInt Value 'notanint' is not an integer"
|
||||
self.assertIn(expected, six.text_type(ex))
|
||||
self.assertIn("Property error : resources.bar.properties.FooInt: "
|
||||
"Value 'notanint' is not an integer",
|
||||
six.text_type(ex))
|
||||
|
||||
# You can turn off value validation via strict_validate
|
||||
stack_novalidate = parser.Stack(utils.dummy_context(), 'test', tmpl,
|
||||
|
@ -328,7 +328,7 @@ class ResourceGroupTest(common.HeatTestCase):
|
||||
resg = resource_group.ResourceGroup('test', snip, stack)
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
resg.validate)
|
||||
errstr = "removal_policies \"'notallowed'\" is not a list"
|
||||
errstr = "removal_policies: \"'notallowed'\" is not a list"
|
||||
self.assertIn(errstr, six.text_type(exc))
|
||||
|
||||
def test_invalid_removal_policies_nomap(self):
|
||||
|
@ -188,8 +188,10 @@ class SaharaNodeGroupTemplateTest(common.HeatTestCase):
|
||||
self.patchobject(ngt, 'is_using_neutron').return_value = False
|
||||
|
||||
ex = self.assertRaises(exception.StackValidationFailed, ngt.validate)
|
||||
self.assertEqual(u"Property error : node-group: flavor Error "
|
||||
u"validating value u'm1.large'", six.text_type(ex))
|
||||
self.assertEqual(u"Property error : "
|
||||
u"resources.node-group.properties.flavor: "
|
||||
u"Error validating value u'm1.large'",
|
||||
six.text_type(ex))
|
||||
|
||||
|
||||
class SaharaClusterTemplateTest(common.HeatTestCase):
|
||||
|
@ -425,8 +425,8 @@ class ServersTest(common.HeatTestCase):
|
||||
create = scheduler.TaskRunner(server.create)
|
||||
error = self.assertRaises(exception.ResourceFailure, create)
|
||||
self.assertEqual(
|
||||
"StackValidationFailed: Property error : WebServer: "
|
||||
"image Error validating value 'Slackware': "
|
||||
"StackValidationFailed: Property error : "
|
||||
"WebServer.Properties.image: Error validating value 'Slackware': "
|
||||
"The Image (Slackware) could not be found.",
|
||||
six.text_type(error))
|
||||
|
||||
@ -450,9 +450,9 @@ class ServersTest(common.HeatTestCase):
|
||||
create = scheduler.TaskRunner(server.create)
|
||||
error = self.assertRaises(exception.ResourceFailure, create)
|
||||
self.assertEqual(
|
||||
'StackValidationFailed: Property error : WebServer: '
|
||||
'image Multiple physical resources were '
|
||||
'found with name (CentOS 5.2).',
|
||||
'StackValidationFailed: Property error : '
|
||||
'WebServer.Properties.image: Multiple physical '
|
||||
'resources were found with name (CentOS 5.2).',
|
||||
six.text_type(error))
|
||||
|
||||
self.m.VerifyAll()
|
||||
@ -474,8 +474,8 @@ class ServersTest(common.HeatTestCase):
|
||||
create = scheduler.TaskRunner(server.create)
|
||||
error = self.assertRaises(exception.ResourceFailure, create)
|
||||
self.assertEqual(
|
||||
"StackValidationFailed: Property error : WebServer: "
|
||||
"image Error validating value '1': "
|
||||
"StackValidationFailed: Property error : "
|
||||
"WebServer.Properties.image: Error validating value '1': "
|
||||
"The Image (1) could not be found.",
|
||||
six.text_type(error))
|
||||
|
||||
@ -1049,9 +1049,9 @@ class ServersTest(common.HeatTestCase):
|
||||
error = self.assertRaises(exception.StackValidationFailed,
|
||||
server.validate)
|
||||
self.assertEqual(
|
||||
"Property error : WebServer: key_name Error validating "
|
||||
"value 'test2': The Key (test2) could not be found.",
|
||||
six.text_type(error))
|
||||
"Property error : Resources.WebServer.Properties.key_name: "
|
||||
"Error validating value 'test2': The Key (test2) could not "
|
||||
"be found.", six.text_type(error))
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_server_validate_with_networks(self):
|
||||
@ -3020,8 +3020,9 @@ class ServersTest(common.HeatTestCase):
|
||||
# update
|
||||
updater = scheduler.TaskRunner(server.update, update_template)
|
||||
err = self.assertRaises(exception.ResourceFailure, updater)
|
||||
self.assertEqual('StackValidationFailed: Property error : WebServer: '
|
||||
'image The Image (Update Image) could not be found.',
|
||||
self.assertEqual('StackValidationFailed: Property error : '
|
||||
'WebServer.Properties.image: The Image '
|
||||
'(Update Image) could not be found.',
|
||||
six.text_type(err))
|
||||
self.m.VerifyAll()
|
||||
|
||||
|
@ -217,8 +217,8 @@ class SoftwareComponentValidationTest(common.HeatTestCase):
|
||||
tool: script
|
||||
''',
|
||||
err=exc.StackValidationFailed,
|
||||
err_msg='actions length (0) is out of range '
|
||||
'(min: 1, max: None)')
|
||||
err_msg='component.properties.configs[0].actions: '
|
||||
'length (0) is out of range (min: 1, max: None)')
|
||||
),
|
||||
(
|
||||
'multiple_configs_per_action_single',
|
||||
|
@ -124,6 +124,9 @@ class TestTemplatePluginManager(common.HeatTestCase):
|
||||
def param_schemata(self):
|
||||
pass
|
||||
|
||||
def get_section_name(self, section):
|
||||
pass
|
||||
|
||||
def parameters(self, stack_identifier, user_params):
|
||||
pass
|
||||
|
||||
|
@ -160,8 +160,8 @@ class TroveClusterTest(common.HeatTestCase):
|
||||
self.rsrc_defn['Properties']['instances'][0]['flavor'] = 'm1.small'
|
||||
tc = trove_cluster.TroveCluster('cluster', self.rsrc_defn, self.stack)
|
||||
ex = self.assertRaises(exception.StackValidationFailed, tc.validate)
|
||||
error_msg = ("Property error : cluster: instances Property error : "
|
||||
"instances: 0 Property error : 0: flavor "
|
||||
error_msg = ("Property error : "
|
||||
"resources.cluster.properties.instances[0].flavor: "
|
||||
"Error validating value 'm1.small': "
|
||||
"The Flavor ID (m1.small) could not be found.")
|
||||
self.assertEqual(error_msg, six.text_type(ex))
|
||||
|
@ -1035,7 +1035,8 @@ class validateTest(common.HeatTestCase):
|
||||
t = template_format.parse(test_template_invalid_property)
|
||||
engine = service.EngineService('a', 't')
|
||||
res = dict(engine.validate_template(None, t, {}))
|
||||
self.assertEqual({'Error': 'Unknown Property UnknownProperty'}, res)
|
||||
self.assertEqual({'Error': 'Property error : WikiDatabase.Properties: '
|
||||
'Unknown Property UnknownProperty'}, res)
|
||||
|
||||
def test_invalid_resources(self):
|
||||
t = template_format.parse(test_template_invalid_resources)
|
||||
@ -1084,7 +1085,8 @@ class validateTest(common.HeatTestCase):
|
||||
engine = service.EngineService('a', 't')
|
||||
res = dict(engine.validate_template(None, t, {}))
|
||||
self.assertEqual(
|
||||
{'Error': 'Property SourceDestCheck not implemented yet'},
|
||||
{'Error': 'Property error : WikiDatabase.Properties: '
|
||||
'Property SourceDestCheck not implemented yet'},
|
||||
res)
|
||||
|
||||
def test_invalid_deletion_policy(self):
|
||||
|
@ -98,7 +98,7 @@ resources:
|
||||
|
||||
# Prove validation works for non-zero create/update
|
||||
template_two_nested = self.template.replace("count: 0", "count: 2")
|
||||
expected_err = "length Value 'BAD' is not an integer"
|
||||
expected_err = "Value 'BAD' is not an integer"
|
||||
ex = self.assertRaises(exc.HTTPBadRequest, self.update_stack,
|
||||
stack_identifier, template_two_nested,
|
||||
environment=env, files=files)
|
||||
|
Loading…
Reference in New Issue
Block a user