checks.py: show warning message for alias option
Show warning message for any alias option find in config instance. Example warning message: WARNING: Config option 'product_name' is deprecated and now an alias to 'release_name', please use 'release_name' instead. In: {'release_name': 'dummy product', 'product_name': 'dummy product'} Signed-off-by: Qixiang Wan <qwan@redhat.com>
This commit is contained in:
parent
9784961568
commit
2aacefd9cd
@ -35,6 +35,7 @@ When a new config option is added, the schema must be updated (see the
|
||||
``CONFIG_DEPS`` mapping.
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import os.path
|
||||
import platform
|
||||
import jsonschema
|
||||
@ -196,6 +197,10 @@ def validate(config):
|
||||
for error in validator.iter_errors(config):
|
||||
if isinstance(error, ConfigDeprecation):
|
||||
warnings.append(REMOVED.format('.'.join(error.path), error.message))
|
||||
elif isinstance(error, ConfigOptionWarning):
|
||||
warnings.append(error.message)
|
||||
elif isinstance(error, ConfigOptionError):
|
||||
errors.append(error.message)
|
||||
elif not error.path and error.validator == 'additionalProperties':
|
||||
allowed_keys = set(error.schema['properties'].keys())
|
||||
used_keys = set(error.instance.keys())
|
||||
@ -241,62 +246,61 @@ def _extend_with_default_and_alias(validator_class):
|
||||
validate_required = validator_class.VALIDATORS['required']
|
||||
validate_additional_properties = validator_class.VALIDATORS['additionalProperties']
|
||||
|
||||
def _replace_alias(properties, instance, schema):
|
||||
@contextlib.contextmanager
|
||||
def _hook_errors(properties, instance, schema):
|
||||
"""
|
||||
If 'alias' is defined for a property, and the property is not present
|
||||
in instance, add the property with value from the alias property to
|
||||
instance before remove the alias property. If both the propery and its
|
||||
alias are present, it will yield an error.
|
||||
Hook the instance and yield errors and warnings.
|
||||
"""
|
||||
errors = []
|
||||
for property, subschema in properties.iteritems():
|
||||
# update instance for alias option
|
||||
# If alias option for the property is present and property is not specified,
|
||||
# update the property in instance with value from alias option.
|
||||
if "alias" in subschema:
|
||||
if property in instance and subschema['alias'] in instance:
|
||||
# the order of validators is in random, so we remove the alias
|
||||
# property at the first time when it's found, then validators
|
||||
# won't raise the same error later.
|
||||
instance.pop(subschema['alias'])
|
||||
yield jsonschema.ValidationError(
|
||||
"%s is an alias of %s, only one can be used." % (
|
||||
subschema['alias'], property)
|
||||
)
|
||||
if subschema['alias'] in instance:
|
||||
msg = "WARNING: Config option '%s' is deprecated and now an alias to '%s', " \
|
||||
"please use '%s' instead. " \
|
||||
"In:\n%s" % (subschema['alias'], property, property, instance)
|
||||
errors.append(ConfigOptionWarning(msg))
|
||||
if property in instance:
|
||||
msg = "ERROR: Config option '%s' is an alias of '%s', only one can be used. In:\n%s" \
|
||||
% (subschema['alias'], property, instance)
|
||||
errors.append(ConfigOptionError(msg))
|
||||
instance.pop(subschema['alias'])
|
||||
else:
|
||||
instance.setdefault(property, instance.pop(subschema['alias']))
|
||||
yield errors
|
||||
|
||||
if property not in instance and subschema['alias'] in instance:
|
||||
instance.setdefault(property, instance.pop(subschema['alias']))
|
||||
|
||||
def set_defaults_and_aliases(validator, properties, instance, schema):
|
||||
def _set_defaults(validator, properties, instance, schema):
|
||||
"""
|
||||
Assign default values to options that have them defined and are not
|
||||
specified. And if a property has 'alias' defined and the property is
|
||||
not specified, look for the alias property and copy alias property's
|
||||
value to that property before remove the alias property.
|
||||
specified.
|
||||
"""
|
||||
for property, subschema in properties.iteritems():
|
||||
if "default" in subschema and property not in instance:
|
||||
instance.setdefault(property, subschema["default"])
|
||||
|
||||
for error in _replace_alias(properties, instance, schema):
|
||||
yield error
|
||||
with _hook_errors(properties, instance, schema) as errors:
|
||||
for error in errors:
|
||||
yield error
|
||||
|
||||
for error in validate_properties(validator, properties, instance, schema):
|
||||
yield error
|
||||
|
||||
def ignore_alias_properties(validator, aP, instance, schema):
|
||||
"""
|
||||
If there is a property has alias defined in schema, and the property is not
|
||||
present in instance, set the property with the value of alias property,
|
||||
remove alias property from instance.
|
||||
"""
|
||||
def _validate_additional_properties(validator, aP, instance, schema):
|
||||
properties = schema.get("properties", {})
|
||||
for error in _replace_alias(properties, instance, schema):
|
||||
yield error
|
||||
with _hook_errors(properties, instance, schema) as errors:
|
||||
for error in errors:
|
||||
yield error
|
||||
|
||||
for error in validate_additional_properties(validator, aP, instance, schema):
|
||||
yield error
|
||||
|
||||
def validate_required_with_alias(validator, required, instance, schema):
|
||||
def _validate_required(validator, required, instance, schema):
|
||||
properties = schema.get("properties", {})
|
||||
for error in _replace_alias(properties, instance, schema):
|
||||
yield error
|
||||
with _hook_errors(properties, instance, schema) as errors:
|
||||
for error in errors:
|
||||
yield error
|
||||
|
||||
for error in validate_required(validator, required, instance, schema):
|
||||
yield error
|
||||
@ -323,11 +327,11 @@ def _extend_with_default_and_alias(validator_class):
|
||||
yield error
|
||||
|
||||
return jsonschema.validators.extend(
|
||||
validator_class, {"properties": set_defaults_and_aliases,
|
||||
validator_class, {"properties": _set_defaults,
|
||||
"deprecated": error_on_deprecated,
|
||||
"type": validate_regex_type,
|
||||
"required": validate_required_with_alias,
|
||||
"additionalProperties": ignore_alias_properties},
|
||||
"required": _validate_required,
|
||||
"additionalProperties": _validate_additional_properties},
|
||||
)
|
||||
|
||||
|
||||
@ -335,6 +339,14 @@ class ConfigDeprecation(jsonschema.exceptions.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class ConfigOptionWarning(jsonschema.exceptions.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class ConfigOptionError(jsonschema.exceptions.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
def _make_schema():
|
||||
return {
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
|
@ -238,7 +238,8 @@ class TestSchemaValidator(unittest.TestCase):
|
||||
config = self._load_conf_from_string(string)
|
||||
errors, warnings = checks.validate(config)
|
||||
self.assertEqual(len(errors), 0)
|
||||
self.assertEqual(len(warnings), 0)
|
||||
self.assertEqual(len(warnings), 1)
|
||||
self.assertRegexpMatches(warnings[0], r"^WARNING: Config option 'product_name' is deprecated and now an alias to 'release_name'.*")
|
||||
self.assertEqual(config.get("release_name", None), "dummy product")
|
||||
|
||||
@mock.patch('pungi.checks._make_schema')
|
||||
@ -285,7 +286,8 @@ class TestSchemaValidator(unittest.TestCase):
|
||||
config = self._load_conf_from_string(string)
|
||||
errors, warnings = checks.validate(config)
|
||||
self.assertEqual(len(errors), 0)
|
||||
self.assertEqual(len(warnings), 0)
|
||||
self.assertEqual(len(warnings), 1)
|
||||
self.assertRegexpMatches(warnings[0], r"^WARNING: Config option 'product_name' is deprecated and now an alias to 'release_name'.*")
|
||||
self.assertEqual(config.get("release_name", None), "dummy product")
|
||||
|
||||
@mock.patch('pungi.checks._make_schema')
|
||||
@ -309,8 +311,9 @@ class TestSchemaValidator(unittest.TestCase):
|
||||
config = self._load_conf_from_string(string)
|
||||
errors, warnings = checks.validate(config)
|
||||
self.assertEqual(len(errors), 1)
|
||||
self.assertIn('Failed validation in : product_name is an alias of release_name, only one can be used.', errors)
|
||||
self.assertEqual(len(warnings), 0)
|
||||
self.assertRegexpMatches(errors[0], r"^ERROR: Config option 'product_name' is an alias of 'release_name', only one can be used.*")
|
||||
self.assertEqual(len(warnings), 1)
|
||||
self.assertRegexpMatches(warnings[0], r"^WARNING: Config option 'product_name' is deprecated and now an alias to 'release_name'.*")
|
||||
self.assertEqual(config.get("release_name", None), "dummy product")
|
||||
|
||||
@mock.patch('pungi.checks._make_schema')
|
||||
@ -348,7 +351,9 @@ class TestSchemaValidator(unittest.TestCase):
|
||||
config = self._load_conf_from_string(string)
|
||||
errors, warnings = checks.validate(config)
|
||||
self.assertEqual(len(errors), 0)
|
||||
self.assertEqual(len(warnings), 0)
|
||||
self.assertEqual(len(warnings), 2)
|
||||
self.assertRegexpMatches(warnings[0], r"^WARNING: Config option '.+' is deprecated and now an alias to '.+'.*")
|
||||
self.assertRegexpMatches(warnings[1], r"^WARNING: Config option '.+' is deprecated and now an alias to '.+'.*")
|
||||
self.assertEqual(config.get("release_name", None), "dummy product")
|
||||
self.assertEqual(config.get("foophase", {}).get("repo", None), "http://www.exampe.com/os")
|
||||
|
||||
|
@ -273,7 +273,7 @@ class OstreeConfigTestCase(ConfigTestCase):
|
||||
"x86_64": {
|
||||
"treefile": "fedora-atomic-docker-host.json",
|
||||
"config_url": "https://git.fedorahosted.org/git/fedora-atomic.git",
|
||||
"source_repo_from": "Everything",
|
||||
"repo_from": "Everything",
|
||||
"ostree_repo": "/mnt/koji/compose/atomic/Rawhide/"
|
||||
}
|
||||
})
|
||||
@ -298,7 +298,7 @@ class OstreeInstallerConfigTestCase(ConfigTestCase):
|
||||
ostree_installer=[
|
||||
("^Atomic$", {
|
||||
"x86_64": {
|
||||
"source_repo_from": "Everything",
|
||||
"repo_from": "Everything",
|
||||
"release": None,
|
||||
"installpkgs": ["fedora-productimg-atomic"],
|
||||
"add_template": ["/spin-kickstarts/atomic-installer/lorax-configure-repo.tmpl"],
|
||||
@ -326,7 +326,7 @@ class OstreeInstallerConfigTestCase(ConfigTestCase):
|
||||
ostree_installer=[
|
||||
("^Atomic$", {
|
||||
"x86_64": {
|
||||
"source_repo_from": "Everything",
|
||||
"repo_from": "Everything",
|
||||
"release": None,
|
||||
"installpkgs": ["fedora-productimg-atomic"],
|
||||
"add_template": ["/spin-kickstarts/atomic-installer/lorax-configure-repo.tmpl"],
|
||||
|
Loading…
Reference in New Issue
Block a user