diff --git a/docs/userguide.rst b/docs/userguide.rst index c192db7..a020f0d 100644 --- a/docs/userguide.rst +++ b/docs/userguide.rst @@ -8,6 +8,7 @@ This projects depends on - Python 3 - distro Python 3 package +- jsonschema Python 3 package - PyYAML Python 3 package (optional, for yaml file support) If you need Python 2 support consider sending patches. The most diff --git a/kas/__init__.py b/kas/__init__.py index 27714fd..4dbc3f1 100644 --- a/kas/__init__.py +++ b/kas/__init__.py @@ -25,6 +25,7 @@ from .__version__ import __version__ from .__version__ import __file_version__, __compatible_file_version__ +from .configschema import CONFIGSCHEMA __license__ = 'MIT' __copyright__ = 'Copyright (c) Siemens AG, 2017' diff --git a/kas/configschema.py b/kas/configschema.py new file mode 100644 index 0000000..d1ebc1f --- /dev/null +++ b/kas/configschema.py @@ -0,0 +1,170 @@ +# kas - setup tool for bitbake based projects +# +# Copyright (c) Siemens AG, 2017 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the 'Software'), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +''' + This module contains the schema of the configuration file. +''' + +CONFIGSCHEMA = { + 'type': 'object', + 'required': ['header'], + 'additionalProperties': False, + 'properties': { + 'header': { + 'type': 'object', + 'required': ['version'], + 'additionalProperties': False, + 'properties': { + 'version': { + 'oneOf': [ + { + 'type': 'string', + 'enum': ['0.10'], + }, + { + 'type': 'integer', + }, + ], + }, + 'includes': { + 'type': 'array', + 'items': { + 'oneOf': [ + { + 'type': 'string', + }, + { + 'type': 'object', + 'required': ['repo', 'file'], + 'additionalProperties': False, + 'properties': { + 'repo': { + 'type': 'string', + }, + 'file': { + 'type': 'string' + }, + }, + }, + ], + }, + }, + }, + }, + 'machine': { + 'type': 'string', + }, + 'distro': { + 'type': 'string', + }, + 'target': { + 'oneOf': [ + { + 'type': 'string', + }, + { + 'type': 'array', + 'items': { + 'type': 'string', + }, + }, + ], + }, + 'task': { + 'type': 'string', + }, + 'repos': { + 'type': 'object', + 'additionalProperties': { + 'oneOf': [ + { + 'type': 'object', + 'additionalProperties': False, + 'properties': { + 'name': { + 'type': 'string', + }, + 'url': { + 'type': 'string', + }, + 'refspec': { + 'type': 'string', + }, + 'path': { + 'type': 'string', + }, + 'layers': { + 'type': 'object', + 'additionalProperties': { + 'oneOf': [ + { + 'type': 'null', + }, + { + 'type': 'integer', + }, + { + 'type': 'boolean', + }, + { + 'type': 'string', + }, + ], + }, + }, + }, + }, + { + 'type': 'null', + }, + ], + }, + }, + 'bblayers_conf_header': { + 'type': 'object', + 'additionalProperties': { + 'type': 'string', + }, + }, + 'local_conf_header': { + 'type': 'object', + 'additionalProperties': { + 'type': 'string', + }, + }, + 'proxy_config': { + 'type': 'object', + 'additionalProperties': False, + 'properties': { + 'http_proxy': { + 'type': 'string', + }, + 'https_proxy': { + 'type': 'string', + }, + 'no_proxy': { + 'type': 'string', + }, + }, + }, + }, +} diff --git a/kas/includehandler.py b/kas/includehandler.py index 2f9a62f..15201bf 100644 --- a/kas/includehandler.py +++ b/kas/includehandler.py @@ -30,7 +30,10 @@ import collections import functools import logging +from jsonschema.validators import Draft4Validator + from . import __file_version__, __compatible_file_version__ +from . import CONFIGSCHEMA __license__ = 'MIT' __copyright__ = 'Copyright (c) Siemens AG, 2017' @@ -62,6 +65,17 @@ def load_config(filename): raise LoadConfigException('Config file extension not recognized', filename) + validator = Draft4Validator(CONFIGSCHEMA) + validation_error = False + + for error in validator.iter_errors(config): + validation_error = True + logging.error('Config file validation Error:\n%s', error) + + if validation_error: + raise LoadConfigException('Errors occured while validating the ' + 'config file %s', filename) + try: header = config.get('header', {}) except AttributeError: diff --git a/setup.py b/setup.py index eedf21f..bb9cbff 100644 --- a/setup.py +++ b/setup.py @@ -84,5 +84,6 @@ setup( install_requires=[ 'PyYAML>=3.0', 'distro>=1.0.0', + 'jsonschema>=2.5.0', ], ) diff --git a/tests/test_includehandler.py b/tests/test_includehandler.py index ea19d24..cfa4633 100644 --- a/tests/test_includehandler.py +++ b/tests/test_includehandler.py @@ -116,7 +116,6 @@ class TestLoadConfig(object): def test_header_valid(self): testvector = [ - 'header: {version: "4"}', 'header: {version: 4}', 'header: {version: 5}', ] @@ -136,7 +135,9 @@ header: version: 5 {}''' - def util_include_content(self, testvector): + def util_include_content(self, testvector, monkeypatch): + # disable schema validation for these tests: + monkeypatch.setattr(includehandler, 'CONFIGSCHEMA', {}) for test in testvector: with patch_open(includehandler, dictionary=test['fdict']): ginc = includehandler.GlobalIncludes('x.yml') @@ -148,7 +149,7 @@ header: assert test['conf'] == config assert test['rmiss'] == missing - def test_valid_includes_none(self): + def test_valid_includes_none(self, monkeypatch): header = self.__class__.header testvector = [ { @@ -164,9 +165,9 @@ header: }, ] - self.util_include_content(testvector) + self.util_include_content(testvector, monkeypatch) - def test_valid_includes_some(self): + def test_valid_includes_some(self, monkeypatch): header = self.__class__.header testvector = [ # Include one file from the same repo: @@ -232,9 +233,9 @@ header: }, ] - self.util_include_content(testvector) + self.util_include_content(testvector, monkeypatch) - def test_valid_overwriting(self): + def test_valid_overwriting(self, monkeypatch): header = self.__class__.header testvector = [ { @@ -293,9 +294,9 @@ v3: }, ] - self.util_include_content(testvector) + self.util_include_content(testvector, monkeypatch) - def test_valid_merging(self): + def test_valid_merging(self, monkeypatch): header = self.__class__.header testvector = [ { @@ -332,9 +333,11 @@ v3: }, ] - self.util_include_content(testvector) + self.util_include_content(testvector, monkeypatch) - def test_valid_ordering(self): + def test_valid_ordering(self, monkeypatch): + # disable schema validation for this test: + monkeypatch.setattr(includehandler, 'CONFIGSCHEMA', {}) header = self.__class__.header with patch_open(includehandler, dictionary={ 'x.yml': header.format(''' includes: ["y.yml", "z.yml"]