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:
parent
b2b5dd919b
commit
50e9294057
@ -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},
|
||||||
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user