|
8 | 8 | import shutil |
9 | 9 | from typing import Any, Dict, List, Optional, Tuple |
10 | 10 |
|
| 11 | +import jsonschema |
11 | 12 | from munch import DefaultMunch |
12 | 13 | import yaml |
13 | 14 | from yamllint import linter |
|
27 | 28 | # It must exist in the root of the repo we're running in. |
28 | 29 | _YAMLLINT_FILE: str = '.yamllint' |
29 | 30 |
|
| 31 | +# The (built-in) Job Definition schema... |
| 32 | +# from the same directory as ours. |
| 33 | +_SCHEMA_FILE: str = os.path.join(os.path.dirname(__file__), 'schema.yaml') |
| 34 | + |
| 35 | +# Load the schema YAML file now. |
| 36 | +# This must work as the file is installed along with this module. |
| 37 | +_JOB_SCHEMA: Dict[str, Any] = {} |
| 38 | +assert os.path.isfile(_SCHEMA_FILE) |
| 39 | +with open(_SCHEMA_FILE, 'r', encoding='utf8') as schema_file: |
| 40 | + _JOB_SCHEMA = yaml.load(schema_file, Loader=yaml.FullLoader) |
| 41 | +assert _JOB_SCHEMA |
| 42 | + |
30 | 43 |
|
31 | 44 | def _print_test_banner(collection: str, |
32 | 45 | job_name: str, |
@@ -64,6 +77,29 @@ def _lint(definition_filename: str) -> bool: |
64 | 77 | return True |
65 | 78 |
|
66 | 79 |
|
| 80 | +def _validate_schema(definition_filename: str) -> bool: |
| 81 | + """Checks the Job Definition against the built-in schema. |
| 82 | + """ |
| 83 | + |
| 84 | + with open(definition_filename, 'rt', encoding='UTF-8') as definition_file: |
| 85 | + job_def: Optional[Dict[str, Any]] =\ |
| 86 | + yaml.load(definition_file, Loader=yaml.FullLoader) |
| 87 | + assert job_def |
| 88 | + |
| 89 | + # Validate the Job Definition against our schema |
| 90 | + try: |
| 91 | + jsonschema.validate(job_def, schema=_JOB_SCHEMA) |
| 92 | + except jsonschema.ValidationError as ex: |
| 93 | + print(f'! Job definition "{definition_filename}"' |
| 94 | + ' does not comply with schema') |
| 95 | + print(f'! Errors is "{ex.message}"') |
| 96 | + print('! Full response follows:') |
| 97 | + print(ex) |
| 98 | + return False |
| 99 | + |
| 100 | + return True |
| 101 | + |
| 102 | + |
67 | 103 | def _check_cwd() -> bool: |
68 | 104 | """Checks the execution directory for sanity (cwd). Here we must find |
69 | 105 | a .yamllint file and a data-manager directory? |
@@ -102,9 +138,15 @@ def _load(skip_lint: bool = False) -> Tuple[List[DefaultMunch], int]: |
102 | 138 | jd_filenames: List[str] = glob.glob(f'{_DEFINITION_DIRECTORY}/*.yaml') |
103 | 139 | for jd_filename in jd_filenames: |
104 | 140 |
|
| 141 | + # Does the definition comply with the dschema, |
| 142 | + # no options here - it must. |
| 143 | + if not _validate_schema(jd_filename): |
| 144 | + return [], -1 |
| 145 | + |
| 146 | + # YAML-lint the definition? |
105 | 147 | if not skip_lint: |
106 | 148 | if not _lint(jd_filename): |
107 | | - return [], -1 |
| 149 | + return [], -2 |
108 | 150 |
|
109 | 151 | with open(jd_filename, 'r', encoding='UTF-8') as jd_file: |
110 | 152 | job_def: Dict[str, Any] = yaml.load(jd_file, Loader=yaml.FullLoader) |
|
0 commit comments