Merge "Allow lists and strings for Json parameters via provider resources"

This commit is contained in:
Jenkins 2015-02-23 22:08:23 +00:00 committed by Gerrit Code Review
commit 32aa3aac2b
2 changed files with 32 additions and 2 deletions

View File

@ -13,6 +13,7 @@
import collections import collections
from oslo_serialization import jsonutils
import six import six
from heat.common import exception from heat.common import exception
@ -58,13 +59,15 @@ class Schema(constr.Schema):
implemented=True, implemented=True,
update_allowed=False, update_allowed=False,
immutable=False, immutable=False,
support_status=support.SupportStatus()): support_status=support.SupportStatus(),
allow_conversion=False):
super(Schema, self).__init__(data_type, description, default, super(Schema, self).__init__(data_type, description, default,
schema, required, constraints) schema, required, constraints)
self.implemented = implemented self.implemented = implemented
self.update_allowed = update_allowed self.update_allowed = update_allowed
self.immutable = immutable self.immutable = immutable
self.support_status = support_status self.support_status = support_status
self.allow_conversion = allow_conversion
# validate structural correctness of schema itself # validate structural correctness of schema itself
self.validate() self.validate()
@ -146,6 +149,13 @@ class Schema(constr.Schema):
param.BOOLEAN: cls.BOOLEAN param.BOOLEAN: cls.BOOLEAN
} }
# allow_conversion allows slightly more flexible type conversion
# where property->parameter types don't align, primarily when
# a json parameter value is passed via a Map property, which requires
# some coercion to pass strings or lists (which are both valid for
# Json parameters but not for Map properties).
allow_conversion = param.type == param.MAP
# make update_allowed true by default on TemplateResources # make update_allowed true by default on TemplateResources
# as the template should deal with this. # as the template should deal with this.
return cls(data_type=param_type_map.get(param.type, cls.MAP), return cls(data_type=param_type_map.get(param.type, cls.MAP),
@ -153,7 +163,8 @@ class Schema(constr.Schema):
required=param.required, required=param.required,
constraints=param.constraints, constraints=param.constraints,
update_allowed=True, update_allowed=True,
immutable=False) immutable=False,
allow_conversion=allow_conversion)
def allowed_param_prop_type(self): def allowed_param_prop_type(self):
""" """
@ -269,6 +280,14 @@ class Property(object):
if value is None: if value is None:
value = self.has_default() and self.default() or {} value = self.has_default() and self.default() or {}
if not isinstance(value, collections.Mapping): if not isinstance(value, collections.Mapping):
# This is to handle passing Lists via Json parameters exposed
# via a provider resource, in particular lists-of-dicts which
# cannot be handled correctly via comma_delimited_list
if self.schema.allow_conversion:
if isinstance(value, six.string_types):
return value
elif isinstance(value, collections.Sequence):
return jsonutils.dumps(value)
raise TypeError(_('"%s" is not a map') % value) raise TypeError(_('"%s" is not a map') % value)
return dict(self._get_children(six.iteritems(value), return dict(self._get_children(six.iteritems(value),

View File

@ -11,6 +11,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from oslo_serialization import jsonutils
import six import six
import testtools import testtools
@ -468,6 +469,7 @@ class PropertySchemaTest(testtools.TestCase):
self.assertTrue(schema.required) self.assertTrue(schema.required)
self.assertIsNone(schema.default) self.assertIsNone(schema.default)
self.assertEqual(0, len(schema.constraints)) self.assertEqual(0, len(schema.constraints))
self.assertFalse(schema.allow_conversion)
def test_from_number_param_min(self): def test_from_number_param_min(self):
default = "42" default = "42"
@ -546,6 +548,7 @@ class PropertySchemaTest(testtools.TestCase):
self.assertIsNone(schema.default) self.assertIsNone(schema.default)
self.assertFalse(schema.required) self.assertFalse(schema.required)
self.assertEqual(1, len(schema.constraints)) self.assertEqual(1, len(schema.constraints))
self.assertFalse(schema.allow_conversion)
allowed_constraint = schema.constraints[0] allowed_constraint = schema.constraints[0]
@ -563,6 +566,7 @@ class PropertySchemaTest(testtools.TestCase):
self.assertEqual(properties.Schema.LIST, schema.type) self.assertEqual(properties.Schema.LIST, schema.type)
self.assertIsNone(schema.default) self.assertIsNone(schema.default)
self.assertFalse(schema.required) self.assertFalse(schema.required)
self.assertFalse(schema.allow_conversion)
def test_from_json_param(self): def test_from_json_param(self):
param = parameters.Schema.from_dict('name', { param = parameters.Schema.from_dict('name', {
@ -575,6 +579,7 @@ class PropertySchemaTest(testtools.TestCase):
self.assertEqual(properties.Schema.MAP, schema.type) self.assertEqual(properties.Schema.MAP, schema.type)
self.assertIsNone(schema.default) self.assertIsNone(schema.default)
self.assertFalse(schema.required) self.assertFalse(schema.required)
self.assertTrue(schema.allow_conversion)
class PropertyTest(testtools.TestCase): class PropertyTest(testtools.TestCase):
@ -862,6 +867,12 @@ class PropertyTest(testtools.TestCase):
p = properties.Property({'Type': 'Map'}) p = properties.Property({'Type': 'Map'})
self.assertRaises(TypeError, p.get_value, ['foo']) self.assertRaises(TypeError, p.get_value, ['foo'])
def test_map_allow_conversion(self):
p = properties.Property({'Type': 'Map'})
p.schema.allow_conversion = True
self.assertEqual('foo', p.get_value('foo'))
self.assertEqual(jsonutils.dumps(['foo']), p.get_value(['foo']))
def test_map_schema_good(self): def test_map_schema_good(self):
map_schema = {'valid': {'Type': 'Boolean'}} map_schema = {'valid': {'Type': 'Boolean'}}
p = properties.Property({'Type': 'Map', 'Schema': map_schema}) p = properties.Property({'Type': 'Map', 'Schema': map_schema})