mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 08:58:59 +00:00
Merge pull request #25515 from s0undt3ch/2015.8
salt.utils.schema fixes
This commit is contained in:
commit
25f910e472
@ -546,7 +546,7 @@ class Schema(six.with_metaclass(SchemaMeta, object)):
|
||||
ordering = []
|
||||
serialized['type'] = 'object'
|
||||
properties = OrderedDict()
|
||||
after_items_update = OrderedDict()
|
||||
cls.after_items_update = []
|
||||
for name in cls._order:
|
||||
skip_order = False
|
||||
if name in cls._sections:
|
||||
@ -558,8 +558,11 @@ class Schema(six.with_metaclass(SchemaMeta, object)):
|
||||
properties.update(serialized_section['properties'])
|
||||
if 'x-ordering' in serialized_section:
|
||||
ordering.extend(serialized_section['x-ordering'])
|
||||
if 'required' in serialized:
|
||||
if 'required' in serialized_section:
|
||||
required.extend(serialized_section['required'])
|
||||
if hasattr(section, 'after_items_update'):
|
||||
cls.after_items_update.extend(section.after_items_update)
|
||||
skip_order = True
|
||||
else:
|
||||
# Store it as a configuration section
|
||||
properties[name] = serialized_section
|
||||
@ -568,7 +571,8 @@ class Schema(six.with_metaclass(SchemaMeta, object)):
|
||||
config = cls._items[name]
|
||||
# Handle the configuration items defined in the class instance
|
||||
if config.__flatten__ is True:
|
||||
after_items_update.update(config.serialize())
|
||||
serialized_config = config.serialize()
|
||||
cls.after_items_update.append(serialized_config)
|
||||
skip_order = True
|
||||
else:
|
||||
properties[name] = config.serialize()
|
||||
@ -586,7 +590,15 @@ class Schema(six.with_metaclass(SchemaMeta, object)):
|
||||
serialized['properties'] = properties
|
||||
|
||||
# Update the serialized object with any items to include after properties
|
||||
serialized.update(after_items_update)
|
||||
if cls.after_items_update:
|
||||
after_items_update = {}
|
||||
for entry in cls.after_items_update:
|
||||
name, data = next(six.iteritems(entry))
|
||||
if name in after_items_update:
|
||||
after_items_update[name].extend(data)
|
||||
else:
|
||||
after_items_update[name] = data
|
||||
serialized.update(after_items_update)
|
||||
|
||||
if required:
|
||||
# Only include required if not empty
|
||||
@ -599,7 +611,12 @@ class Schema(six.with_metaclass(SchemaMeta, object)):
|
||||
|
||||
@classmethod
|
||||
def as_requirements_item(cls):
|
||||
return RequirementsItem(requirements=cls())
|
||||
serialized_schema = cls.serialize()
|
||||
required = serialized_schema.get('required', [])
|
||||
for name in serialized_schema['properties']:
|
||||
if name not in required:
|
||||
required.append(name)
|
||||
return RequirementsItem(requirements=required)
|
||||
|
||||
#@classmethod
|
||||
#def render_as_rst(cls):
|
||||
@ -1253,34 +1270,29 @@ class RequirementsItem(SchemaItem):
|
||||
raise RuntimeError(
|
||||
'The passed requirements must not be empty'
|
||||
)
|
||||
if not isinstance(self.requirements, (Schema, SchemaItem, list, tuple, set)):
|
||||
if not isinstance(self.requirements, (SchemaItem, list, tuple, set)):
|
||||
raise RuntimeError(
|
||||
'The passed requirements must be passed as a list, tuple, '
|
||||
'set SchemaItem, BaseSchemaItem or Schema, not \'{0}\''.format(self.requirements)
|
||||
'set SchemaItem or BaseSchemaItem, not \'{0}\''.format(self.requirements)
|
||||
)
|
||||
|
||||
if not isinstance(self.requirements, (SchemaItem, Schema)):
|
||||
if not isinstance(self.requirements, SchemaItem):
|
||||
if not isinstance(self.requirements, list):
|
||||
self.requirements = list(self.requirements)
|
||||
|
||||
for idx, item in enumerate(self.requirements):
|
||||
if not isinstance(item, (six.string_types, (SchemaItem, Schema))):
|
||||
if not isinstance(item, (six.string_types, SchemaItem)):
|
||||
raise RuntimeError(
|
||||
'The passed requirement at the {0} index must be of type '
|
||||
'str or Schema, not \'{1}\''.format(idx, type(item))
|
||||
'str or SchemaItem, not \'{1}\''.format(idx, type(item))
|
||||
)
|
||||
|
||||
def serialize(self):
|
||||
if isinstance(self.requirements, Schema):
|
||||
requirements = self.requirements.serialize()['required']
|
||||
elif isinstance(self.requirements, SchemaItem):
|
||||
if isinstance(self.requirements, SchemaItem):
|
||||
requirements = self.requirements.serialize()
|
||||
else:
|
||||
requirements = []
|
||||
for requirement in self.requirements:
|
||||
if isinstance(requirement, Schema):
|
||||
requirements.extend(requirement.serialize()['required'])
|
||||
continue
|
||||
if isinstance(requirement, SchemaItem):
|
||||
requirements.append(requirement.serialize())
|
||||
continue
|
||||
|
@ -80,6 +80,43 @@ class ConfigTestCase(TestCase):
|
||||
}
|
||||
)
|
||||
|
||||
class MergedConfigClass(schema.Schema):
|
||||
thirsty = schema.BooleanItem(title='Thirsty', description='Are you thirsty?', required=True)
|
||||
merge_subclassed = SubClassedConfig(flatten=True)
|
||||
|
||||
expected = {
|
||||
'$schema': 'http://json-schema.org/draft-04/schema#',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'thirsty': {
|
||||
'type': 'boolean',
|
||||
'description': 'Are you thirsty?',
|
||||
'title': 'Thirsty'
|
||||
},
|
||||
'base': {
|
||||
'default': True,
|
||||
'type': 'boolean',
|
||||
'title': 'base'
|
||||
},
|
||||
'hungry': {
|
||||
'type': 'boolean',
|
||||
'description': 'Are you hungry?',
|
||||
'title': 'Hungry'
|
||||
}
|
||||
},
|
||||
'required': ['thirsty', 'base', 'hungry'],
|
||||
'x-ordering': ['thirsty', 'base', 'hungry'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
self.assertDictContainsSubset(
|
||||
MergedConfigClass.serialize()['properties'],
|
||||
expected['properties']
|
||||
)
|
||||
self.assertDictContainsSubset(
|
||||
expected,
|
||||
MergedConfigClass.serialize()
|
||||
)
|
||||
|
||||
def test_configuration_items_order(self):
|
||||
|
||||
class One(schema.Schema):
|
||||
@ -103,7 +140,7 @@ class ConfigTestCase(TestCase):
|
||||
title='SSH Private Key',
|
||||
description='The path to an SSH private key which will be used '
|
||||
'to authenticate on the deployed VMs',
|
||||
required=True)
|
||||
)
|
||||
|
||||
class SSHKeyNamesSchema(schema.Schema):
|
||||
ssh_key_names = schema.StringItem(
|
||||
@ -111,7 +148,7 @@ class ConfigTestCase(TestCase):
|
||||
description='The names of an SSH key being managed on '
|
||||
'Digital Ocean account which will be used to '
|
||||
'authenticate on the deployed VMs',
|
||||
required=True)
|
||||
)
|
||||
|
||||
class Requirements(BaseRequirements):
|
||||
title = 'Digital Ocean'
|
||||
@ -132,54 +169,277 @@ class ConfigTestCase(TestCase):
|
||||
ssh_key_file = SSHKeyFileSchema(flatten=True)
|
||||
ssh_key_names = SSHKeyNamesSchema(flatten=True)
|
||||
|
||||
expexcted = {
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "Digital Ocean",
|
||||
"description": "Digital Ocean Cloud VM configuration requirements.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"driver": {
|
||||
"default": "digital_ocean",
|
||||
"format": "hidden",
|
||||
"type": "string",
|
||||
"title": "driver"
|
||||
},
|
||||
"personal_access_token": {
|
||||
"type": "string",
|
||||
"description": "This is the API access token which can be "
|
||||
"generated under the API/Application on your account",
|
||||
"title": "Personal Access Token"
|
||||
},
|
||||
"ssh_key_file": {
|
||||
"type": "string",
|
||||
"description": "The path to an SSH private key which will "
|
||||
"be used to authenticate on the deployed VMs",
|
||||
"title": "SSH Private Key"
|
||||
},
|
||||
"ssh_key_names": {
|
||||
"type": "string",
|
||||
"description": "The names of an SSH key being managed on Digital "
|
||||
"Ocean account which will be used to authenticate "
|
||||
"on the deployed VMs",
|
||||
"title": "SSH Key Names"
|
||||
}
|
||||
expected = {
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "Digital Ocean",
|
||||
"description": "Digital Ocean Cloud VM configuration requirements.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"driver": {
|
||||
"default": "digital_ocean",
|
||||
"format": "hidden",
|
||||
"type": "string",
|
||||
"title": "driver"
|
||||
},
|
||||
"anyOf": [
|
||||
{"required": ["ssh_key_file"]},
|
||||
{"required": ["ssh_key_names"]}
|
||||
],
|
||||
"required": [
|
||||
"personal_access_token"
|
||||
],
|
||||
"x-ordering": [
|
||||
"driver",
|
||||
"personal_access_token",
|
||||
"ssh_key_file",
|
||||
"ssh_key_names",
|
||||
],
|
||||
"additionalProperties": False
|
||||
}
|
||||
self.assertDictEqual(expexcted, Requirements.serialize())
|
||||
"personal_access_token": {
|
||||
"type": "string",
|
||||
"description": "This is the API access token which can be "
|
||||
"generated under the API/Application on your account",
|
||||
"title": "Personal Access Token"
|
||||
},
|
||||
"ssh_key_file": {
|
||||
"type": "string",
|
||||
"description": "The path to an SSH private key which will "
|
||||
"be used to authenticate on the deployed VMs",
|
||||
"title": "SSH Private Key"
|
||||
},
|
||||
"ssh_key_names": {
|
||||
"type": "string",
|
||||
"description": "The names of an SSH key being managed on Digital "
|
||||
"Ocean account which will be used to authenticate "
|
||||
"on the deployed VMs",
|
||||
"title": "SSH Key Names"
|
||||
}
|
||||
},
|
||||
"anyOf": [
|
||||
{"required": ["ssh_key_file"]},
|
||||
{"required": ["ssh_key_names"]}
|
||||
],
|
||||
"required": [
|
||||
"personal_access_token"
|
||||
],
|
||||
"x-ordering": [
|
||||
"driver",
|
||||
"personal_access_token",
|
||||
"ssh_key_file",
|
||||
"ssh_key_names",
|
||||
],
|
||||
"additionalProperties": False
|
||||
}
|
||||
self.assertDictEqual(expected, Requirements.serialize())
|
||||
|
||||
class Requirements2(BaseRequirements):
|
||||
title = 'Digital Ocean'
|
||||
description = 'Digital Ocean Cloud VM configuration requirements.'
|
||||
|
||||
personal_access_token = schema.StringItem(
|
||||
title='Personal Access Token',
|
||||
description='This is the API access token which can be generated '
|
||||
'under the API/Application on your account',
|
||||
required=True)
|
||||
|
||||
ssh_key_file = schema.StringItem(
|
||||
title='SSH Private Key',
|
||||
description='The path to an SSH private key which will be used '
|
||||
'to authenticate on the deployed VMs')
|
||||
|
||||
ssh_key_names = schema.StringItem(
|
||||
title='SSH Key Names',
|
||||
description='The names of an SSH key being managed on '
|
||||
'Digital Ocean account which will be used to '
|
||||
'authenticate on the deployed VMs')
|
||||
|
||||
requirements_definition = schema.AnyOfItem(
|
||||
items=(
|
||||
schema.RequirementsItem(requirements=['ssh_key_file']),
|
||||
schema.RequirementsItem(requirements=['ssh_key_names'])
|
||||
),
|
||||
)(flatten=True)
|
||||
|
||||
expected = {
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "Digital Ocean",
|
||||
"description": "Digital Ocean Cloud VM configuration requirements.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"driver": {
|
||||
"default": "digital_ocean",
|
||||
"format": "hidden",
|
||||
"type": "string",
|
||||
"title": "driver"
|
||||
},
|
||||
"personal_access_token": {
|
||||
"type": "string",
|
||||
"description": "This is the API access token which can be "
|
||||
"generated under the API/Application on your account",
|
||||
"title": "Personal Access Token"
|
||||
},
|
||||
"ssh_key_file": {
|
||||
"type": "string",
|
||||
"description": "The path to an SSH private key which will "
|
||||
"be used to authenticate on the deployed VMs",
|
||||
"title": "SSH Private Key"
|
||||
},
|
||||
"ssh_key_names": {
|
||||
"type": "string",
|
||||
"description": "The names of an SSH key being managed on Digital "
|
||||
"Ocean account which will be used to authenticate "
|
||||
"on the deployed VMs",
|
||||
"title": "SSH Key Names"
|
||||
}
|
||||
},
|
||||
"anyOf": [
|
||||
{"required": ["ssh_key_file"]},
|
||||
{"required": ["ssh_key_names"]}
|
||||
],
|
||||
"required": [
|
||||
"personal_access_token"
|
||||
],
|
||||
"x-ordering": [
|
||||
"driver",
|
||||
"personal_access_token",
|
||||
"ssh_key_file",
|
||||
"ssh_key_names",
|
||||
],
|
||||
"additionalProperties": False
|
||||
}
|
||||
self.assertDictContainsSubset(expected, Requirements2.serialize())
|
||||
|
||||
class Requirements3(schema.Schema):
|
||||
title = 'Digital Ocean'
|
||||
description = 'Digital Ocean Cloud VM configuration requirements.'
|
||||
|
||||
merge_reqs = Requirements(flatten=True)
|
||||
|
||||
expected = {
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "Digital Ocean",
|
||||
"description": "Digital Ocean Cloud VM configuration requirements.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"driver": {
|
||||
"default": "digital_ocean",
|
||||
"format": "hidden",
|
||||
"type": "string",
|
||||
"title": "driver"
|
||||
},
|
||||
"personal_access_token": {
|
||||
"type": "string",
|
||||
"description": "This is the API access token which can be "
|
||||
"generated under the API/Application on your account",
|
||||
"title": "Personal Access Token"
|
||||
},
|
||||
"ssh_key_file": {
|
||||
"type": "string",
|
||||
"description": "The path to an SSH private key which will "
|
||||
"be used to authenticate on the deployed VMs",
|
||||
"title": "SSH Private Key"
|
||||
},
|
||||
"ssh_key_names": {
|
||||
"type": "string",
|
||||
"description": "The names of an SSH key being managed on Digital "
|
||||
"Ocean account which will be used to authenticate "
|
||||
"on the deployed VMs",
|
||||
"title": "SSH Key Names"
|
||||
}
|
||||
},
|
||||
"anyOf": [
|
||||
{"required": ["ssh_key_file"]},
|
||||
{"required": ["ssh_key_names"]}
|
||||
],
|
||||
"required": [
|
||||
"personal_access_token"
|
||||
],
|
||||
"x-ordering": [
|
||||
"driver",
|
||||
"personal_access_token",
|
||||
"ssh_key_file",
|
||||
"ssh_key_names",
|
||||
],
|
||||
"additionalProperties": False
|
||||
}
|
||||
self.assertDictContainsSubset(expected, Requirements3.serialize())
|
||||
|
||||
class Requirements4(schema.Schema):
|
||||
title = 'Digital Ocean'
|
||||
description = 'Digital Ocean Cloud VM configuration requirements.'
|
||||
|
||||
merge_reqs = Requirements(flatten=True)
|
||||
|
||||
ssh_key_file_2 = schema.StringItem(
|
||||
title='SSH Private Key',
|
||||
description='The path to an SSH private key which will be used '
|
||||
'to authenticate on the deployed VMs')
|
||||
|
||||
ssh_key_names_2 = schema.StringItem(
|
||||
title='SSH Key Names',
|
||||
description='The names of an SSH key being managed on '
|
||||
'Digital Ocean account which will be used to '
|
||||
'authenticate on the deployed VMs')
|
||||
|
||||
requirements_definition_2 = schema.AnyOfItem(
|
||||
items=(
|
||||
schema.RequirementsItem(requirements=['ssh_key_file_2']),
|
||||
schema.RequirementsItem(requirements=['ssh_key_names_2'])
|
||||
),
|
||||
)(flatten=True)
|
||||
|
||||
expected = {
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "Digital Ocean",
|
||||
"description": "Digital Ocean Cloud VM configuration requirements.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"driver": {
|
||||
"default": "digital_ocean",
|
||||
"format": "hidden",
|
||||
"type": "string",
|
||||
"title": "driver"
|
||||
},
|
||||
"personal_access_token": {
|
||||
"type": "string",
|
||||
"description": "This is the API access token which can be "
|
||||
"generated under the API/Application on your account",
|
||||
"title": "Personal Access Token"
|
||||
},
|
||||
"ssh_key_file": {
|
||||
"type": "string",
|
||||
"description": "The path to an SSH private key which will "
|
||||
"be used to authenticate on the deployed VMs",
|
||||
"title": "SSH Private Key"
|
||||
},
|
||||
"ssh_key_names": {
|
||||
"type": "string",
|
||||
"description": "The names of an SSH key being managed on Digital "
|
||||
"Ocean account which will be used to authenticate "
|
||||
"on the deployed VMs",
|
||||
"title": "SSH Key Names"
|
||||
},
|
||||
"ssh_key_file_2": {
|
||||
"type": "string",
|
||||
"description": "The path to an SSH private key which will "
|
||||
"be used to authenticate on the deployed VMs",
|
||||
"title": "SSH Private Key"
|
||||
},
|
||||
"ssh_key_names_2": {
|
||||
"type": "string",
|
||||
"description": "The names of an SSH key being managed on Digital "
|
||||
"Ocean account which will be used to authenticate "
|
||||
"on the deployed VMs",
|
||||
"title": "SSH Key Names"
|
||||
}
|
||||
},
|
||||
"anyOf": [
|
||||
{"required": ["ssh_key_file"]},
|
||||
{"required": ["ssh_key_names"]},
|
||||
{"required": ["ssh_key_file_2"]},
|
||||
{"required": ["ssh_key_names_2"]}
|
||||
],
|
||||
"required": [
|
||||
"personal_access_token"
|
||||
],
|
||||
"x-ordering": [
|
||||
"driver",
|
||||
"personal_access_token",
|
||||
"ssh_key_file",
|
||||
"ssh_key_names",
|
||||
"ssh_key_file_2",
|
||||
"ssh_key_names_2",
|
||||
],
|
||||
"additionalProperties": False
|
||||
}
|
||||
self.assertDictContainsSubset(expected, Requirements4.serialize())
|
||||
|
||||
|
||||
@skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
|
||||
def test_optional_requirements_config_validation(self):
|
||||
@ -190,16 +450,14 @@ class ConfigTestCase(TestCase):
|
||||
ssh_key_file = schema.StringItem(
|
||||
title='SSH Private Key',
|
||||
description='The path to an SSH private key which will be used '
|
||||
'to authenticate on the deployed VMs',
|
||||
required=True)
|
||||
'to authenticate on the deployed VMs')
|
||||
|
||||
class SSHKeyNamesSchema(schema.Schema):
|
||||
ssh_key_names = schema.StringItem(
|
||||
title='SSH Key Names',
|
||||
description='The names of an SSH key being managed on '
|
||||
'Digial Ocean account which will be used to '
|
||||
'authenticate on the deployed VMs',
|
||||
required=True)
|
||||
'authenticate on the deployed VMs')
|
||||
|
||||
class Requirements(BaseRequirements):
|
||||
title = 'Digital Ocean'
|
||||
|
Loading…
Reference in New Issue
Block a user