From 48214930bbec2e09569d848fa5b8759c429acd5b Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Wed, 1 Oct 2014 15:26:45 +0200 Subject: [PATCH] Convert bool/int values into string for string properties Instead of rising an exception, boolean and integer values are converted into string if string is expected. Change-Id: I1e0014576c4df447bfa1595c530f1e24f411195d Closes-Bug: #1367375 --- heat/engine/properties.py | 5 ++++- heat/tests/generic_resource.py | 3 ++- heat/tests/test_autoscaling.py | 20 -------------------- heat/tests/test_engine_service.py | 6 ++++++ heat/tests/test_event.py | 2 +- heat/tests/test_instance.py | 24 ------------------------ heat/tests/test_properties.py | 29 ++++++++++++++++++++++++----- 7 files changed, 37 insertions(+), 52 deletions(-) diff --git a/heat/engine/properties.py b/heat/engine/properties.py index 5168ed002..4f8c9db68 100644 --- a/heat/engine/properties.py +++ b/heat/engine/properties.py @@ -243,7 +243,10 @@ class Property(object): if value is None: value = self.has_default() and self.default() or '' if not isinstance(value, basestring): - raise ValueError(_('Value must be a string')) + if isinstance(value, (bool, int)): + value = six.text_type(value) + else: + raise ValueError(_('Value must be a string')) return value def _get_children(self, child_values, keys=None, validate=False): diff --git a/heat/tests/generic_resource.py b/heat/tests/generic_resource.py index 130e0577d..9843b14f6 100644 --- a/heat/tests/generic_resource.py +++ b/heat/tests/generic_resource.py @@ -74,7 +74,8 @@ class ResWithComplexPropsAndAttrs(GenericResource): class ResourceWithProps(GenericResource): - properties_schema = {'Foo': {'Type': 'String'}} + properties_schema = {'Foo': {'Type': 'String'}, + 'FooInt': {'Type': 'Integer'}} class ResourceWithPropsAndAttrs(ResourceWithProps): diff --git a/heat/tests/test_autoscaling.py b/heat/tests/test_autoscaling.py index ed0d66081..17d820905 100644 --- a/heat/tests/test_autoscaling.py +++ b/heat/tests/test_autoscaling.py @@ -1589,26 +1589,6 @@ class AutoScalingTest(common.HeatTestCase): self.assertIsNone(rsrc.resource_id) self.assertEqual('LaunchConfig', rsrc.FnGetRefId()) - def test_validate_BlockDeviceMappings_VolumeSize_invalid_str(self): - t = template_format.parse(as_template) - lcp = t['Resources']['LaunchConfig']['Properties'] - bdm = [{'DeviceName': 'vdb', - 'Ebs': {'SnapshotId': '1234', - 'VolumeSize': 10}}] - lcp['BlockDeviceMappings'] = bdm - stack = utils.parse_stack(t, params=self.params) - self.stub_ImageConstraint_validate() - self.m.ReplayAll() - - e = self.assertRaises(exception.StackValidationFailed, - self.create_scaling_group, t, - stack, 'LaunchConfig') - - expected_msg = "Value must be a string" - self.assertIn(expected_msg, six.text_type(e)) - - self.m.VerifyAll() - def test_validate_BlockDeviceMappings_without_Ebs_property(self): t = template_format.parse(as_template) lcp = t['Resources']['LaunchConfig']['Properties'] diff --git a/heat/tests/test_engine_service.py b/heat/tests/test_engine_service.py index ccc49e115..ab77034ab 100644 --- a/heat/tests/test_engine_service.py +++ b/heat/tests/test_engine_service.py @@ -2234,6 +2234,12 @@ class StackServiceTest(HeatTestCase): 'update_allowed': False, 'immutable': False, }, + 'FooInt': { + 'type': 'integer', + 'required': False, + 'update_allowed': False, + 'immutable': False, + }, }, 'attributes': { 'foo': {'description': 'A generic attribute'}, diff --git a/heat/tests/test_event.py b/heat/tests/test_event.py index 1f756a568..638b0f685 100644 --- a/heat/tests/test_event.py +++ b/heat/tests/test_event.py @@ -149,7 +149,7 @@ class EventTest(HeatTestCase): rname = 'bad_resource' defn = rsrc_defn.ResourceDefinition(rname, 'ResourceWithRequiredProps', - {'Foo': False}) + {'IntFoo': False}) res = generic_rsrc.ResourceWithRequiredProps(rname, defn, self.stack) e = event.Event(self.ctx, self.stack, 'TEST', 'IN_PROGRESS', 'Testing', diff --git a/heat/tests/test_instance.py b/heat/tests/test_instance.py index 92698c362..e52198999 100644 --- a/heat/tests/test_instance.py +++ b/heat/tests/test_instance.py @@ -225,30 +225,6 @@ class InstancesTest(HeatTestCase): self.m.VerifyAll() - def test_validate_BlockDeviceMappings_VolumeSize_invalid_str(self): - stack_name = 'val_VolumeSize_valid' - tmpl, stack = self._setup_test_stack(stack_name) - bdm = [{'DeviceName': 'vdb', - 'Ebs': {'SnapshotId': '1234', - 'VolumeSize': 10}}] - wsp = tmpl.t['Resources']['WebServer']['Properties'] - wsp['BlockDeviceMappings'] = bdm - resource_defns = tmpl.resource_definitions(stack) - instance = instances.Instance('validate_volume_size', - resource_defns['WebServer'], stack) - - self._mock_get_image_id_success('F17-x86_64-gold', 1) - self.m.StubOutWithMock(nova.NovaClientPlugin, '_create') - nova.NovaClientPlugin._create().MultipleTimes().AndReturn(self.fc) - - self.m.ReplayAll() - - exc = self.assertRaises(exception.StackValidationFailed, - instance.validate) - self.assertIn("Value must be a string", six.text_type(exc)) - - self.m.VerifyAll() - def test_validate_BlockDeviceMappings_without_Ebs_property(self): stack_name = 'without_Ebs' tmpl, stack = self._setup_test_stack(stack_name) diff --git a/heat/tests/test_properties.py b/heat/tests/test_properties.py index 751186287..84f7f22bd 100644 --- a/heat/tests/test_properties.py +++ b/heat/tests/test_properties.py @@ -703,6 +703,16 @@ class PropertyTest(testtools.TestCase): self.assertEqual("int() argument must be a string or a number, " "not 'list'", six.text_type(ex)) + def test_str_from_int(self): + schema = {'Type': 'String'} + p = properties.Property(schema) + self.assertEqual('3', p.get_value(3)) + + def test_str_from_bool(self): + schema = {'Type': 'String'} + p = properties.Property(schema) + self.assertEqual('True', p.get_value(True)) + def test_int_from_str_good(self): schema = {'Type': 'Integer'} p = properties.Property(schema) @@ -1498,16 +1508,25 @@ class PropertiesValidationTest(testtools.TestCase): props = properties.Properties(schema, {}) self.assertIsNone(props.validate()) - def test_bad_data(self): - schema = {'foo': {'Type': 'String'}} - props = properties.Properties(schema, {'foo': 42}) - self.assertRaises(exception.StackValidationFailed, props.validate) - def test_unknown_typo(self): schema = {'foo': {'Type': 'String'}} props = properties.Properties(schema, {'food': 42}) self.assertRaises(exception.StackValidationFailed, props.validate) + def test_list_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', + 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', + six.text_type(ex)) + def test_none_string(self): schema = {'foo': {'Type': 'String'}} props = properties.Properties(schema, {'foo': None})