Allow lists and strings for Json parameters via provider resources
Currently we have a somewhat bogus internal equivalence between the
"json" parameter type and the MAP property type. This is a problem
when exposing json parameters via provider resources, because the
schema of the exposed property does not exactly match the capabilities
of the underlying parameter.
Specifically, json parameters can take strings (json serialized list or
map), and also lists and maps directly. Currently, we can only pass
maps, which means some data, for example a list of maps (which is possible
when interacting directly with a json parameter) is not possible when
using the same parameter abstracted via a TemplateResource.
To work around this add a flag which relaxes the type rules and enables
some coercion of map values, only when they're going via a schema derived
from a json parameter.
Changes required for backport:
* Changed import of oslo_serialization.jsonutils to
heat.openstack.common.jsonutils.
Change-Id: I8eff36c4343a07b644b84aa9f2f74eceeb62b4a9
Closes-Bug: #1420196
(cherry-picked from 413fde3247
)
This commit is contained in:
parent
0083fb09ca
commit
40bfa60d84
|
@ -20,6 +20,7 @@ from heat.engine import constraints as constr
|
|||
from heat.engine import function
|
||||
from heat.engine import parameters
|
||||
from heat.engine import support
|
||||
from heat.openstack.common import jsonutils
|
||||
|
||||
SCHEMA_KEYS = (
|
||||
REQUIRED, IMPLEMENTED, DEFAULT, TYPE, SCHEMA,
|
||||
|
@ -57,13 +58,15 @@ class Schema(constr.Schema):
|
|||
implemented=True,
|
||||
update_allowed=False,
|
||||
immutable=False,
|
||||
support_status=support.SupportStatus()):
|
||||
support_status=support.SupportStatus(),
|
||||
allow_conversion=False):
|
||||
super(Schema, self).__init__(data_type, description, default,
|
||||
schema, required, constraints)
|
||||
self.implemented = implemented
|
||||
self.update_allowed = update_allowed
|
||||
self.immutable = immutable
|
||||
self.support_status = support_status
|
||||
self.allow_conversion = allow_conversion
|
||||
# validate structural correctness of schema itself
|
||||
self.validate()
|
||||
|
||||
|
@ -145,6 +148,13 @@ class Schema(constr.Schema):
|
|||
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
|
||||
# as the template should deal with this.
|
||||
return cls(data_type=param_type_map.get(param.type, cls.MAP),
|
||||
|
@ -152,7 +162,8 @@ class Schema(constr.Schema):
|
|||
required=param.required,
|
||||
constraints=param.constraints,
|
||||
update_allowed=True,
|
||||
immutable=False)
|
||||
immutable=False,
|
||||
allow_conversion=allow_conversion)
|
||||
|
||||
def allowed_param_prop_type(self):
|
||||
"""
|
||||
|
@ -268,6 +279,14 @@ class Property(object):
|
|||
if value is None:
|
||||
value = self.has_default() and self.default() or {}
|
||||
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)
|
||||
|
||||
return dict(self._get_children(six.iteritems(value),
|
||||
|
|
|
@ -22,6 +22,7 @@ from heat.engine import parameters
|
|||
from heat.engine import properties
|
||||
from heat.engine import resources
|
||||
from heat.engine import support
|
||||
from heat.openstack.common import jsonutils
|
||||
|
||||
|
||||
class PropertySchemaTest(testtools.TestCase):
|
||||
|
@ -468,6 +469,7 @@ class PropertySchemaTest(testtools.TestCase):
|
|||
self.assertTrue(schema.required)
|
||||
self.assertIsNone(schema.default)
|
||||
self.assertEqual(0, len(schema.constraints))
|
||||
self.assertFalse(schema.allow_conversion)
|
||||
|
||||
def test_from_number_param_min(self):
|
||||
default = "42"
|
||||
|
@ -546,6 +548,7 @@ class PropertySchemaTest(testtools.TestCase):
|
|||
self.assertIsNone(schema.default)
|
||||
self.assertFalse(schema.required)
|
||||
self.assertEqual(1, len(schema.constraints))
|
||||
self.assertFalse(schema.allow_conversion)
|
||||
|
||||
allowed_constraint = schema.constraints[0]
|
||||
|
||||
|
@ -563,6 +566,7 @@ class PropertySchemaTest(testtools.TestCase):
|
|||
self.assertEqual(properties.Schema.LIST, schema.type)
|
||||
self.assertIsNone(schema.default)
|
||||
self.assertFalse(schema.required)
|
||||
self.assertFalse(schema.allow_conversion)
|
||||
|
||||
def test_from_json_param(self):
|
||||
param = parameters.Schema.from_dict('name', {
|
||||
|
@ -575,6 +579,7 @@ class PropertySchemaTest(testtools.TestCase):
|
|||
self.assertEqual(properties.Schema.MAP, schema.type)
|
||||
self.assertIsNone(schema.default)
|
||||
self.assertFalse(schema.required)
|
||||
self.assertTrue(schema.allow_conversion)
|
||||
|
||||
|
||||
class PropertyTest(testtools.TestCase):
|
||||
|
@ -862,6 +867,12 @@ class PropertyTest(testtools.TestCase):
|
|||
p = properties.Property({'Type': 'Map'})
|
||||
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):
|
||||
map_schema = {'valid': {'Type': 'Boolean'}}
|
||||
p = properties.Property({'Type': 'Map', 'Schema': map_schema})
|
||||
|
|
Loading…
Reference in New Issue