config: Validate variant regular expressions

When the mapping in configuration specifies incorrect regular expression
to match regular expressions, we should raise an error immediately and
not wait until the part of config is actually used.

This patch does not cover `live_media`, `image_build` and `osbs`
sections, as they use plain dicts and not the list of tuples format.

Fixes: #424
Signed-off-by: Lubomír Sedlář <lsedlar@redhat.com>
This commit is contained in:
Lubomír Sedlář 2016-10-12 09:38:59 +02:00
parent b2b5dd919b
commit 50e9294057
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 platform
import jsonschema
import re
from . import util
@ -174,7 +175,9 @@ def validate(config):
"""
schema = _make_schema()
DefaultValidator = _extend_with_default(jsonschema.Draft4Validator)
validator = DefaultValidator(schema, {'array': (tuple, list)})
validator = DefaultValidator(schema,
{'array': (tuple, list),
'regex': (str, unicode)})
errors = []
for error in validator.iter_errors(config):
if isinstance(error, ConfigDeprecation):
@ -220,8 +223,13 @@ UNKNOWN_SUGGEST = 'Unrecognized config option: {0}. Did you mean {1}?'
def _extend_with_default(validator_class):
validate_properties = validator_class.VALIDATORS["properties"]
validate_type = validator_class.VALIDATORS['type']
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():
if "default" in subschema and property not in instance:
instance.setdefault(property, subschema["default"])
@ -230,13 +238,32 @@ def _extend_with_default(validator_class):
yield error
def error_on_deprecated(validator, properties, instance, schema):
"""Unconditionally raise deprecation error if encountered."""
yield ConfigDeprecation(
'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(
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,
},
"package_mapping": {
"type": "array",
"items": {
"type": "array",
"items": [
{
"type": "string",
},
{
"type": "object",
"patternProperties": {
".+": {"$ref": "#/definitions/list_of_strings"},
},
"additionalProperties": False,
}
],
"additionalItems": False,
},
},
"package_mapping": _variant_arch_mapping(
{"$ref": "#/definitions/list_of_strings"}
),
"scm_dict": {
"type": "object",
@ -618,6 +629,9 @@ def _make_schema():
"live_media": {
"type": "object",
"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",
"items": {
@ -695,6 +709,9 @@ def _make_schema():
"image_build": {
"type": "object",
"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",
"items": {
@ -764,6 +781,9 @@ def _make_schema():
"osbs": {
"type": "object",
"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",
"properties": {
@ -839,7 +859,7 @@ def _variant_arch_mapping(value):
"items": {
"type": "array",
"items": [
{"type": "string"},
{"type": "regex"},
{
"type": "object",
"patternProperties": {".+": value},

View File

@ -379,5 +379,16 @@ class TestSuggestions(unittest.TestCase):
[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__':
unittest.main()