checks.py: support 'append' option

If 'append' is defined for a property, append the values from append
options to the property. Note: The property must support to be a list
of values.

For example:

with schema:

    schema = {
        "$schema": "http://json-schema.org/draft-04/schema#",
        "title": "Pungi Configuration",
        "type": "object",
        "definitions": {
            "list_of_strings": {
                "type": "array",
                "items": {"type": "string"},
            },
            "strings": {
                "anyOf": [
                    {"type": "string"},
                    {"$ref": "#/definitions/list_of_strings"},
                ]
            },
        },
        "properties": {
            "release_name": {"type": "string"},
            "repo": {"$ref": "#/definitions/strings", "append": "repo_from"}
        },
        "additionalProperties": False,
    }

and config:

    repo = "http://url/to/repo"
    repo_from = "Server"

config will be updated to:

    repo = ["http://url/to/repo", "Server"]

It supports multiple append options too, like:

    "repo": {
        "$ref": "#/definitions/strings",
        "append": ["repo_from", "source_repo_from"],
    }

Signed-off-by: Qixiang Wan <qwan@redhat.com>
This commit is contained in:
Qixiang Wan 2017-03-24 13:19:31 -06:00
parent 2aacefd9cd
commit d1763fca7e
2 changed files with 149 additions and 2 deletions

View File

@ -40,6 +40,7 @@ import os.path
import platform
import jsonschema
import re
from kobo.shortcuts import force_list
from . import util
@ -263,12 +264,34 @@ def _extend_with_default_and_alias(validator_class):
"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)
msg = "ERROR: Config option '%s' is an alias of '%s', only one can be used." \
% (subschema['alias'], property)
errors.append(ConfigOptionError(msg))
instance.pop(subschema['alias'])
else:
instance.setdefault(property, instance.pop(subschema['alias']))
# update instance for append option
# If append is defined in schema, append values from append options to property. If property
# is not present in instance, set it to empty list, and append the values from append options.
# Note: property's schema must support a list of values.
if "append" in subschema:
appends = force_list(subschema['append'])
for append in appends:
if append in instance:
msg = "WARNING: Config option '%s' is deprecated, its value will be appended to option '%s'. " \
"In:\n%s" % (append, property, instance)
errors.append(ConfigOptionWarning(msg))
if property in instance:
msg = "WARNING: Value from config option '%s' is now appended to option '%s'." \
% (append, property)
errors.append(ConfigOptionWarning(msg))
instance[property] = force_list(instance[property])
instance[property].extend(force_list(instance.pop(append)))
else:
msg = "WARNING: Config option '%s' is not found, but '%s' is specified, value from '%s' " \
"is now added as '%s'." % (property, append, append, property)
errors.append(ConfigOptionWarning(msg))
instance[property] = instance.pop(append)
yield errors
def _set_defaults(validator, properties, instance, schema):

View File

@ -357,6 +357,130 @@ class TestSchemaValidator(unittest.TestCase):
self.assertEqual(config.get("release_name", None), "dummy product")
self.assertEqual(config.get("foophase", {}).get("repo", None), "http://www.exampe.com/os")
@mock.patch('pungi.checks._make_schema')
def test_append_option(self, make_schema):
schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Pungi Configuration",
"type": "object",
"definitions": {
"list_of_strings": {
"type": "array",
"items": {"type": "string"},
},
"strings": {
"anyOf": [
{"type": "string"},
{"$ref": "#/definitions/list_of_strings"},
]
},
},
"properties": {
"release_name": {"type": "string"},
"repo": {"$ref": "#/definitions/strings", "append": "repo_from"}
},
"additionalProperties": False,
}
make_schema.return_value = schema
string = """
release_name = "dummy product"
repo = "http://url/to/repo"
repo_from = 'Server'
"""
config = self._load_conf_from_string(string)
errors, warnings = checks.validate(config)
self.assertEqual(len(errors), 0)
self.assertEqual(len(warnings), 2)
self.assertRegexpMatches(warnings[0], r"^WARNING: Config option 'repo_from' is deprecated, its value will be appended to option 'repo'.*")
self.assertRegexpMatches(warnings[1], r"^WARNING: Value from config option 'repo_from' is now appended to option 'repo'")
self.assertEqual(config.get("release_name", None), "dummy product")
self.assertEqual(config.get("repo", None), ["http://url/to/repo", "Server"])
@mock.patch('pungi.checks._make_schema')
def test_append_to_nonexist_option(self, make_schema):
schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Pungi Configuration",
"type": "object",
"definitions": {
"list_of_strings": {
"type": "array",
"items": {"type": "string"},
},
"strings": {
"anyOf": [
{"type": "string"},
{"$ref": "#/definitions/list_of_strings"},
]
},
},
"properties": {
"release_name": {"type": "string"},
"repo": {"$ref": "#/definitions/strings", "append": "repo_from"}
},
"additionalProperties": False,
}
make_schema.return_value = schema
string = """
release_name = "dummy product"
repo_from = ['http://url/to/repo', 'Server']
"""
config = self._load_conf_from_string(string)
errors, warnings = checks.validate(config)
self.assertEqual(len(errors), 0)
self.assertEqual(len(warnings), 2)
self.assertRegexpMatches(warnings[0], r"^WARNING: Config option 'repo_from' is deprecated, its value will be appended to option 'repo'.*")
self.assertRegexpMatches(warnings[1], r"^WARNING: Config option 'repo' is not found, but 'repo_from' is specified,")
self.assertEqual(config.get("release_name", None), "dummy product")
self.assertEqual(config.get("repo", None), ["http://url/to/repo", "Server"])
@mock.patch('pungi.checks._make_schema')
def test_multiple_appends(self, make_schema):
schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Pungi Configuration",
"type": "object",
"definitions": {
"list_of_strings": {
"type": "array",
"items": {"type": "string"},
},
"strings": {
"anyOf": [
{"type": "string"},
{"$ref": "#/definitions/list_of_strings"},
]
},
},
"properties": {
"release_name": {"type": "string"},
"repo": {
"$ref": "#/definitions/strings",
"append": ["repo_from", "source_repo_from"]
}
},
"additionalProperties": False,
}
make_schema.return_value = schema
string = """
release_name = "dummy product"
repo_from = ['http://url/to/repo', 'Server']
source_repo_from = 'Client'
"""
config = self._load_conf_from_string(string)
errors, warnings = checks.validate(config)
self.assertEqual(len(errors), 0)
self.assertEqual(len(warnings), 4)
self.assertRegexpMatches(warnings[0], r"^WARNING: Config option 'repo_from' is deprecated, its value will be appended to option 'repo'.*")
self.assertRegexpMatches(warnings[1], r"^WARNING: Config option 'repo' is not found, but 'repo_from' is specified,")
self.assertRegexpMatches(warnings[2], r"^WARNING: Config option 'source_repo_from' is deprecated, its value will be appended to option 'repo'")
self.assertRegexpMatches(warnings[3], r"^WARNING: Value from config option 'source_repo_from' is now appended to option 'repo'.")
self.assertEqual(config.get("release_name", None), "dummy product")
self.assertEqual(config.get("repo", None), ["http://url/to/repo", "Server", "Client"])
class TestUmask(unittest.TestCase):
def setUp(self):