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:
parent
2aacefd9cd
commit
d1763fca7e
@ -40,6 +40,7 @@ import os.path
|
|||||||
import platform
|
import platform
|
||||||
import jsonschema
|
import jsonschema
|
||||||
import re
|
import re
|
||||||
|
from kobo.shortcuts import force_list
|
||||||
|
|
||||||
from . import util
|
from . import util
|
||||||
|
|
||||||
@ -263,12 +264,34 @@ def _extend_with_default_and_alias(validator_class):
|
|||||||
"In:\n%s" % (subschema['alias'], property, property, instance)
|
"In:\n%s" % (subschema['alias'], property, property, instance)
|
||||||
errors.append(ConfigOptionWarning(msg))
|
errors.append(ConfigOptionWarning(msg))
|
||||||
if property in instance:
|
if property in instance:
|
||||||
msg = "ERROR: Config option '%s' is an alias of '%s', only one can be used. In:\n%s" \
|
msg = "ERROR: Config option '%s' is an alias of '%s', only one can be used." \
|
||||||
% (subschema['alias'], property, instance)
|
% (subschema['alias'], property)
|
||||||
errors.append(ConfigOptionError(msg))
|
errors.append(ConfigOptionError(msg))
|
||||||
instance.pop(subschema['alias'])
|
instance.pop(subschema['alias'])
|
||||||
else:
|
else:
|
||||||
instance.setdefault(property, instance.pop(subschema['alias']))
|
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
|
yield errors
|
||||||
|
|
||||||
def _set_defaults(validator, properties, instance, schema):
|
def _set_defaults(validator, properties, instance, schema):
|
||||||
|
@ -357,6 +357,130 @@ class TestSchemaValidator(unittest.TestCase):
|
|||||||
self.assertEqual(config.get("release_name", None), "dummy product")
|
self.assertEqual(config.get("release_name", None), "dummy product")
|
||||||
self.assertEqual(config.get("foophase", {}).get("repo", None), "http://www.exampe.com/os")
|
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):
|
class TestUmask(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user