Merge #425 config: Validate variant regular expressions

This commit is contained in:
Dennis Gilmore 2016-10-19 16:32:39 +00:00
commit bb933c83ae
2 changed files with 53 additions and 22 deletions

View File

@ -38,6 +38,7 @@ When a new config option is added, the schema must be updated (see the
import os.path import os.path
import platform import platform
import jsonschema import jsonschema
import re
from . import util from . import util
@ -174,7 +175,9 @@ def validate(config):
""" """
schema = _make_schema() schema = _make_schema()
DefaultValidator = _extend_with_default(jsonschema.Draft4Validator) DefaultValidator = _extend_with_default(jsonschema.Draft4Validator)
validator = DefaultValidator(schema, {'array': (tuple, list)}) validator = DefaultValidator(schema,
{'array': (tuple, list),
'regex': (str, unicode)})
errors = [] errors = []
for error in validator.iter_errors(config): for error in validator.iter_errors(config):
if isinstance(error, ConfigDeprecation): if isinstance(error, ConfigDeprecation):
@ -220,8 +223,13 @@ UNKNOWN_SUGGEST = 'Unrecognized config option: {0}. Did you mean {1}?'
def _extend_with_default(validator_class): def _extend_with_default(validator_class):
validate_properties = validator_class.VALIDATORS["properties"] validate_properties = validator_class.VALIDATORS["properties"]
validate_type = validator_class.VALIDATORS['type']
def set_defaults(validator, properties, instance, schema): def set_defaults(validator, properties, instance, schema):
"""
Assign default values to options that have them defined and are not
specified.
"""
for property, subschema in properties.iteritems(): for property, subschema in properties.iteritems():
if "default" in subschema and property not in instance: if "default" in subschema and property not in instance:
instance.setdefault(property, subschema["default"]) instance.setdefault(property, subschema["default"])
@ -230,13 +238,32 @@ def _extend_with_default(validator_class):
yield error yield error
def error_on_deprecated(validator, properties, instance, schema): def error_on_deprecated(validator, properties, instance, schema):
"""Unconditionally raise deprecation error if encountered."""
yield ConfigDeprecation( yield ConfigDeprecation(
'use %s instead' % properties 'use %s instead' % properties
) )
def validate_regex_type(validator, properties, instance, schema):
"""
Extend standard type validation to check correctness in regular
expressions.
"""
if properties == 'regex':
try:
re.compile(instance)
except re.error as exc:
yield jsonschema.ValidationError(
'incorrect regular expression: %s' % str(exc),
)
else:
# Not a regular expression, delegate to original validator.
for error in validate_type(validator, properties, instance, schema):
yield error
return jsonschema.validators.extend( return jsonschema.validators.extend(
validator_class, {"properties": set_defaults, validator_class, {"properties": set_defaults,
"deprecated": error_on_deprecated}, "deprecated": error_on_deprecated,
"type": validate_regex_type},
) )
@ -258,25 +285,9 @@ def _make_schema():
"additionalProperties": False, "additionalProperties": False,
}, },
"package_mapping": { "package_mapping": _variant_arch_mapping(
"type": "array", {"$ref": "#/definitions/list_of_strings"}
"items": { ),
"type": "array",
"items": [
{
"type": "string",
},
{
"type": "object",
"patternProperties": {
".+": {"$ref": "#/definitions/list_of_strings"},
},
"additionalProperties": False,
}
],
"additionalItems": False,
},
},
"scm_dict": { "scm_dict": {
"type": "object", "type": "object",
@ -618,6 +629,9 @@ def _make_schema():
"live_media": { "live_media": {
"type": "object", "type": "object",
"patternProperties": { "patternProperties": {
# Warning: this pattern is a variant uid regex, but the
# format does not let us validate it as there is no regular
# expression to describe all regular expressions.
".+": { ".+": {
"type": "array", "type": "array",
"items": { "items": {
@ -695,6 +709,9 @@ def _make_schema():
"image_build": { "image_build": {
"type": "object", "type": "object",
"patternProperties": { "patternProperties": {
# Warning: this pattern is a variant uid regex, but the
# format does not let us validate it as there is no regular
# expression to describe all regular expressions.
".+": { ".+": {
"type": "array", "type": "array",
"items": { "items": {
@ -764,6 +781,9 @@ def _make_schema():
"osbs": { "osbs": {
"type": "object", "type": "object",
"patternProperties": { "patternProperties": {
# Warning: this pattern is a variant uid regex, but the
# format does not let us validate it as there is no regular
# expression to describe all regular expressions.
".+": { ".+": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -839,7 +859,7 @@ def _variant_arch_mapping(value):
"items": { "items": {
"type": "array", "type": "array",
"items": [ "items": [
{"type": "string"}, {"type": "regex"},
{ {
"type": "object", "type": "object",
"patternProperties": {".+": value}, "patternProperties": {".+": value},

View File

@ -379,5 +379,16 @@ class TestSuggestions(unittest.TestCase):
[checks.UNKNOWN_SUGGEST.format('product_pid', 'product_id')]) [checks.UNKNOWN_SUGGEST.format('product_pid', 'product_id')])
class TestRegexValidation(unittest.TestCase):
def test_incorrect_regular_expression(self):
cfg = load_config(PKGSET_REPOS,
multilib=[('^*$', {'*': []})])
self.assertEqual(
checks.validate(cfg),
['Failed validation in multilib.0.0: incorrect regular '
'expression: nothing to repeat'])
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()