Add --schema-override to pungi-config-validate script
Some composes might need extra validation to ensure they are following certain strict rules - for example containing only signed packages or packages only from particular Koji tag. There is currently no way how to check that Pungi configuration fulfills these extra requirements. This commit adds new `--schema-override` option to `pungi-config-validate` script which allows caller to specify path to JSON schema overriding the default JSON schema and therefore limitting it further. For exmaple, to limit the `pkgset_source` to `koji`, one can use following JSON schema override: ``` { "properties": { "pkgset_source": { "enum": ["koji"] } } } ``` It is possible to use `--schema-override` multiple times to apply multiple schema overrides. Merges: https://pagure.io/pungi/pull-request/1341 Signed-off-by: Jan Kaluza <jkaluza@redhat.com>
This commit is contained in:
parent
6f23c7b8ba
commit
ef33d00f5b
@ -185,12 +185,12 @@ def _check_dep(name, value, lst, matcher, fmt):
|
|||||||
yield fmt.format(name, value, dep)
|
yield fmt.format(name, value, dep)
|
||||||
|
|
||||||
|
|
||||||
def validate(config, offline=False):
|
def validate(config, offline=False, schema=None):
|
||||||
"""Test the configuration against schema.
|
"""Test the configuration against schema.
|
||||||
|
|
||||||
Undefined values for which a default value exists will be filled in.
|
Undefined values for which a default value exists will be filled in.
|
||||||
"""
|
"""
|
||||||
schema = make_schema()
|
schema = schema or make_schema()
|
||||||
DefaultValidator = _extend_with_default_and_alias(
|
DefaultValidator = _extend_with_default_and_alias(
|
||||||
jsonschema.Draft4Validator, offline=offline
|
jsonschema.Draft4Validator, offline=offline
|
||||||
)
|
)
|
||||||
@ -1442,6 +1442,25 @@ CONFIG_DEPS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def update_schema(schema, update_dict):
|
||||||
|
"""
|
||||||
|
Updates the leaf values in `schema` by the leaf values from `update_dict`.
|
||||||
|
|
||||||
|
This is different from `schema.update(update_dict)`, because
|
||||||
|
the `schema.update()` would override also non-leaf values.
|
||||||
|
|
||||||
|
:param schema: Schema to update.
|
||||||
|
:param updated_dict: Dict matching the schema with updated leaf values.
|
||||||
|
:returns: Updated schema
|
||||||
|
"""
|
||||||
|
for k, v in update_dict.items():
|
||||||
|
if isinstance(v, dict):
|
||||||
|
schema[k] = update_schema(schema.get(k, {}), v)
|
||||||
|
else:
|
||||||
|
schema[k] = v
|
||||||
|
return schema
|
||||||
|
|
||||||
|
|
||||||
def _get_gather_backends():
|
def _get_gather_backends():
|
||||||
if six.PY2:
|
if six.PY2:
|
||||||
return ['yum', 'dnf']
|
return ['yum', 'dnf']
|
||||||
|
@ -72,7 +72,7 @@ def read_variants(compose, config):
|
|||||||
compose.all_variants[child.uid] = child
|
compose.all_variants[child.uid] = child
|
||||||
|
|
||||||
|
|
||||||
def run(config, topdir, has_old, offline, defined_variables):
|
def run(config, topdir, has_old, offline, defined_variables, schema_overrides):
|
||||||
# Load default values for undefined variables. This is useful for
|
# Load default values for undefined variables. This is useful for
|
||||||
# validating templates that are supposed to be filled in later with
|
# validating templates that are supposed to be filled in later with
|
||||||
# pungi-config-dump.
|
# pungi-config-dump.
|
||||||
@ -89,7 +89,13 @@ def run(config, topdir, has_old, offline, defined_variables):
|
|||||||
# Remove the dummy variables used for defaults.
|
# Remove the dummy variables used for defaults.
|
||||||
config_utils.remove_unknown(conf, defined_variables)
|
config_utils.remove_unknown(conf, defined_variables)
|
||||||
|
|
||||||
errors, warnings = pungi.checks.validate(conf, offline=offline)
|
# Load extra schemas JSON files.
|
||||||
|
schema = pungi.checks.make_schema()
|
||||||
|
for schema_override in schema_overrides:
|
||||||
|
with open(schema_override) as f:
|
||||||
|
schema = pungi.checks.update_schema(schema, json.load(f))
|
||||||
|
|
||||||
|
errors, warnings = pungi.checks.validate(conf, offline=offline, schema=schema)
|
||||||
if errors or warnings:
|
if errors or warnings:
|
||||||
for error in errors + warnings:
|
for error in errors + warnings:
|
||||||
print(error)
|
print(error)
|
||||||
@ -169,11 +175,21 @@ def main(args=None):
|
|||||||
"Can be used multiple times."
|
"Can be used multiple times."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--schema-override',
|
||||||
|
action="append",
|
||||||
|
default=[],
|
||||||
|
help=(
|
||||||
|
'Path to extra JSON schema defining the values which will override '
|
||||||
|
'the original Pungi JSON schema values.'
|
||||||
|
),
|
||||||
|
)
|
||||||
opts = parser.parse_args(args)
|
opts = parser.parse_args(args)
|
||||||
defines = config_utils.extract_defines(opts.define)
|
defines = config_utils.extract_defines(opts.define)
|
||||||
|
|
||||||
with pungi.util.temp_dir() as topdir:
|
with pungi.util.temp_dir() as topdir:
|
||||||
errors = run(opts.config, topdir, opts.old_composes, opts.offline, defines)
|
errors = run(opts.config, topdir, opts.old_composes, opts.offline, defines,
|
||||||
|
opts.schema_override)
|
||||||
|
|
||||||
for msg in errors:
|
for msg in errors:
|
||||||
print(msg)
|
print(msg)
|
||||||
|
7
tests/data/dummy-override.json
Normal file
7
tests/data/dummy-override.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"pkgset_source": {
|
||||||
|
"enum": ["koji"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -542,6 +542,31 @@ class TestSchemaValidator(unittest.TestCase):
|
|||||||
self.assertEqual(config["foo"], "git://example.com/repo.git#HEAD")
|
self.assertEqual(config["foo"], "git://example.com/repo.git#HEAD")
|
||||||
self.assertEqual(resolve_git_url.call_args_list, [])
|
self.assertEqual(resolve_git_url.call_args_list, [])
|
||||||
|
|
||||||
|
def test_update_schema(self):
|
||||||
|
schema = checks.make_schema()
|
||||||
|
schema_override = {
|
||||||
|
"definitions": {
|
||||||
|
"scm_dict": {
|
||||||
|
"properties": {
|
||||||
|
"scm": {
|
||||||
|
"enum": ["git"]
|
||||||
|
},
|
||||||
|
"repo": {
|
||||||
|
"enum": ["git://localhost/pungi-fedora.git"]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
schema = checks.update_schema(schema, schema_override)
|
||||||
|
scm_dict_properties = schema["definitions"]["scm_dict"]["properties"]
|
||||||
|
self.assertEqual(
|
||||||
|
scm_dict_properties["scm"],
|
||||||
|
{'enum': ['git'], 'type': 'string'})
|
||||||
|
self.assertEqual(
|
||||||
|
scm_dict_properties["repo"],
|
||||||
|
{'enum': ['git://localhost/pungi-fedora.git'], 'type': 'string'})
|
||||||
|
self.assertEqual(scm_dict_properties["file"], {"type": "string"})
|
||||||
|
|
||||||
class TestUmask(unittest.TestCase):
|
class TestUmask(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -13,6 +13,7 @@ from tests import helpers
|
|||||||
|
|
||||||
HERE = os.path.abspath(os.path.dirname(__file__))
|
HERE = os.path.abspath(os.path.dirname(__file__))
|
||||||
DUMMY_CONFIG = os.path.join(HERE, 'data/dummy-pungi.conf')
|
DUMMY_CONFIG = os.path.join(HERE, 'data/dummy-pungi.conf')
|
||||||
|
SCHEMA_OVERRIDE = os.path.join(HERE, 'data/dummy-override.json')
|
||||||
|
|
||||||
|
|
||||||
class ConfigValidateScriptTest(helpers.PungiTestCase):
|
class ConfigValidateScriptTest(helpers.PungiTestCase):
|
||||||
@ -24,3 +25,16 @@ class ConfigValidateScriptTest(helpers.PungiTestCase):
|
|||||||
cli_main()
|
cli_main()
|
||||||
self.assertEqual('', stdout.getvalue())
|
self.assertEqual('', stdout.getvalue())
|
||||||
self.assertEqual('', stderr.getvalue())
|
self.assertEqual('', stderr.getvalue())
|
||||||
|
|
||||||
|
@mock.patch('sys.argv', new=[
|
||||||
|
'pungi-config-validate', DUMMY_CONFIG, "--schema-override",
|
||||||
|
SCHEMA_OVERRIDE])
|
||||||
|
@mock.patch('sys.stderr', new_callable=six.StringIO)
|
||||||
|
@mock.patch('sys.stdout', new_callable=six.StringIO)
|
||||||
|
@mock.patch('sys.exit')
|
||||||
|
def test_schema_override(self, exit, stdout, stderr):
|
||||||
|
cli_main()
|
||||||
|
self.assertTrue(stdout.getvalue().startswith(
|
||||||
|
"Failed validation in pkgset_source: 'repos' is not one of"))
|
||||||
|
self.assertEqual('', stderr.getvalue())
|
||||||
|
exit.assert_called_once_with(1)
|
||||||
|
Loading…
Reference in New Issue
Block a user