config: Don't abort on deprecated options

These options are in fact removed and have no effect anymore. This patch
changes the validation to print a warning that the option was removed
and what should be done instead. It no longer stops the whole compose.

The validation script still rejects configuration files with these
removed keys.

This change means we no longer check these removals with the JSON schema
(as that makes it hard to determine where exactly the problem is).

Fixes: #438
Signed-off-by: Lubomír Sedlář <lsedlar@redhat.com>
This commit is contained in:
Lubomír Sedlář 2016-10-20 10:49:28 +02:00
parent 6e55cc6419
commit c38bb4809b
4 changed files with 41 additions and 42 deletions

View File

@ -65,8 +65,9 @@ def run(config, topdir, has_old):
conf.load_from_file(config) conf.load_from_file(config)
errors = pungi.checks.validate(conf) errors = pungi.checks.validate(conf)
if errors: warnings = list(pungi.checks.report_removed(conf))
for error in errors: if errors or warnings:
for error in errors + warnings:
print(error) print(error)
sys.exit(1) sys.exit(1)

View File

@ -174,6 +174,8 @@ def main():
sys.exit(1) sys.exit(1)
pungi.checks.check_umask(logger) pungi.checks.check_umask(logger)
errors = pungi.checks.validate(conf) errors = pungi.checks.validate(conf)
for warning in pungi.checks.report_removed(conf):
print >>sys.stderr, warning
if errors: if errors:
for error in errors: for error in errors:
print >>sys.stderr, error print >>sys.stderr, error

View File

@ -180,24 +180,29 @@ def validate(config):
'regex': (str, unicode)}) 'regex': (str, unicode)})
errors = [] errors = []
for error in validator.iter_errors(config): for error in validator.iter_errors(config):
if isinstance(error, ConfigDeprecation): if not error.path and error.validator == 'additionalProperties':
errors.append(DEPRECATED.format('.'.join(error.path), error.message)) allowed_keys = set(error.schema['properties'].keys())
used_keys = set(error.instance.keys())
for key in used_keys - allowed_keys:
suggestion = _get_suggestion(key, allowed_keys)
if suggestion:
errors.append(UNKNOWN_SUGGEST.format(key, suggestion))
else:
errors.append(UNKNOWN.format(key))
else: else:
if not error.path and error.validator == 'additionalProperties': errors.append('Failed validation in %s: %s' % (
allowed_keys = set(error.schema['properties'].keys()) '.'.join([str(x) for x in error.path]), error.message))
used_keys = set(error.instance.keys())
for key in used_keys - allowed_keys:
suggestion = _get_suggestion(key, allowed_keys)
if suggestion:
errors.append(UNKNOWN_SUGGEST.format(key, suggestion))
else:
errors.append(UNKNOWN.format(key))
else:
errors.append('Failed validation in %s: %s' % (
'.'.join([str(x) for x in error.path]), error.message))
return errors + _validate_requires(schema, config, CONFIG_DEPS) return errors + _validate_requires(schema, config, CONFIG_DEPS)
def report_removed(config):
schema = _make_schema()
for key in config:
msg = schema['properties'].get(key, {}).get('deprecated')
if msg:
yield REMOVED.format(key, msg)
def _get_suggestion(desired, names): def _get_suggestion(desired, names):
"""Find a value in ``names`` that is the closest match for ``desired``. """Find a value in ``names`` that is the closest match for ``desired``.
@ -214,11 +219,11 @@ def _get_suggestion(desired, names):
return closest return closest
CONFLICTS = 'Config option {0}={1} conflicts with option {2}.' CONFLICTS = 'ERROR: Config option {0}={1} conflicts with option {2}.'
REQUIRES = 'Config option {0}={1} requires {2} which is not set.' REQUIRES = 'ERROR: Config option {0}={1} requires {2} which is not set.'
DEPRECATED = 'Deprecated config option: {0}; {1}.' REMOVED = 'WARNING: Config option {0} was removed and has no effect; {1}.'
UNKNOWN = 'Unrecognized config option: {0}.' UNKNOWN = 'ERROR: Unrecognized config option: {0}.'
UNKNOWN_SUGGEST = 'Unrecognized config option: {0}. Did you mean {1}?' UNKNOWN_SUGGEST = 'ERROR: Unrecognized config option: {0}. Did you mean {1}?'
def _extend_with_default(validator_class): def _extend_with_default(validator_class):
@ -237,12 +242,6 @@ def _extend_with_default(validator_class):
for error in validate_properties(validator, properties, instance, schema): for error in validate_properties(validator, properties, instance, schema):
yield error 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): def validate_regex_type(validator, properties, instance, schema):
""" """
Extend standard type validation to check correctness in regular Extend standard type validation to check correctness in regular
@ -262,15 +261,10 @@ def _extend_with_default(validator_class):
return jsonschema.validators.extend( return jsonschema.validators.extend(
validator_class, {"properties": set_defaults, validator_class, {"properties": set_defaults,
"deprecated": error_on_deprecated,
"type": validate_regex_type}, "type": validate_regex_type},
) )
class ConfigDeprecation(jsonschema.exceptions.ValidationError):
pass
def _make_schema(): def _make_schema():
return { return {
"$schema": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#",
@ -588,7 +582,7 @@ def _make_schema():
"default": True, "default": True,
}, },
"keep_original_comps": { "keep_original_comps": {
"deprecated": "no <groups> tag for respective variant in variants XML" "deprecated": "remove <groups> tag from respective variant in variants XML"
}, },
"link_type": { "link_type": {
@ -822,25 +816,25 @@ def _make_schema():
# Deprecated options # Deprecated options
"multilib_arches": { "multilib_arches": {
"deprecated": "multilib" "deprecated": "use multilib instead"
}, },
"multilib_methods": { "multilib_methods": {
"deprecated": "multilib" "deprecated": "use multilib instead"
}, },
"additional_packages_multiarch": { "additional_packages_multiarch": {
"deprecated": "multilib_whitelist" "deprecated": "use multilib_whitelist instead"
}, },
"filter_packages_multiarch": { "filter_packages_multiarch": {
"deprecated": "multilib_blacklist" "deprecated": "use multilib_blacklist instead"
}, },
"buildinstall_upgrade_image": { "buildinstall_upgrade_image": {
"deprecated": "lorax_options" "deprecated": "use lorax_options instead"
}, },
"pkgset_koji_path_prefix": { "pkgset_koji_path_prefix": {
"deprecated": "koji_profile", "deprecated": "use koji_profile instead",
}, },
"pkgset_koji_url": { "pkgset_koji_url": {
"deprecated": "koji_profile", "deprecated": "use koji_profile instead",
}, },
}, },

View File

@ -194,9 +194,11 @@ class BuildinstallConfigTestCase(unittest.TestCase):
buildinstall_upgrade_image=True, buildinstall_upgrade_image=True,
) )
self.assertItemsEqual(checks.validate(cfg), [])
self.assertItemsEqual( self.assertItemsEqual(
checks.validate(cfg), checks.report_removed(cfg),
[checks.DEPRECATED.format('buildinstall_upgrade_image', 'use lorax_options instead')] [checks.REMOVED.format('buildinstall_upgrade_image', 'use lorax_options instead')]
) )