File: configvalidator.py

package info (click to toggle)
calamares 3.3.14-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 32,444 kB
  • sloc: cpp: 71,554; python: 4,328; xml: 1,379; sh: 866; ansic: 105; makefile: 7
file content (135 lines) | stat: -rwxr-xr-x 3,981 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#! /usr/bin/env python3
#
# SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
# SPDX-License-Identifier: BSD-2-Clause
#
usage = """
Validates a Calamares config file -- YAML syntax -- against a schema.

The schema is also written in YAML syntax, but the schema itself
is JSON-schema. This is possible because all JSON is YAML, and most
YAML is JSON. The limited subset of YAML that Calamares uses is
JSON-representable, anyway.

Usage:
    configvalidator.py <schema> <file> ...
    configvalidator.py -m <module>
    configvalidator.py -x

Exits with value 0 on success, otherwise:
    1 on missing dependencies
    2 on invalid command-line arguments
    3 on missing files
    4 if files have invalid syntax
    5 if files fail to validate
Use -x as only command-line argument to check the imports only.

Use -m <module> as shorthand for standard paths in src/modules/<module>/
"""

# The schemata originally lived outside the Calamares repository,
# without documented tooling. By putting them in the repository
# with the example files and explicit tooling, there's a better
# chance of them catching problems and acting as documentation.

dependencies = """
Dependencies for this tool are: py-yaml and py-jsonschema.

    https://pyyaml.org/
    https://github.com/Julian/jsonschema

Simple installation is `pip install pyyaml jsonschema`
"""

ERR_IMPORT, ERR_USAGE, ERR_FILE_NOT_FOUND, ERR_SYNTAX, ERR_INVALID = range(1,6)

### DEPENDENCIES
#
#
try:
    from jsonschema import validate, SchemaError, ValidationError
    from yaml import safe_load, YAMLError
except ImportError as e:
    print(e)
    print(dependencies)
    exit(ERR_IMPORT)

from os.path import exists
import sys

### INPUT VALIDATION
#
#
if len(sys.argv) < 3:
    # Special-case: called with -x to just test the imports
    if len(sys.argv) == 2 and sys.argv[1] == "-x":
        exit(0)
    print(usage)
    exit(ERR_USAGE)

if len(sys.argv) == 3 and sys.argv[1] == "-m":
    module = sys.argv[2]
    schema_file_name = f"src/modules/{module}/{module}.schema.yaml"
    config_file_names = [ f"src/modules/{module}/{module}.conf" ]
else:
    schema_file_name = sys.argv[1]
    config_file_names = sys.argv[2:]

if not exists(schema_file_name):
    print(usage)
    print("\nSchema file '{}' does not exist.".format(schema_file_name))
    exit(ERR_FILE_NOT_FOUND)
for f in config_file_names:
    if not exists(f):
        print(usage)
        print("\nYAML file '{}' does not exist.".format(f))
        exit(ERR_FILE_NOT_FOUND)

### FILES SYNTAX CHECK
#
#
with open(schema_file_name, "r") as data:
    try:
        schema = safe_load(data)
    except YAMLError as e:
        print("Schema error: {} {}.".format(e.problem, e.problem_mark))
        print("\nSchema file '{}' is invalid YAML.".format(schema_file_name))
        exit(ERR_SYNTAX)

try:
    validate(instance={}, schema=schema)
# While developing the schemata, get full exceptions from schema failure
except SchemaError as e:
    print(e)
    print("\nSchema file '{}' is invalid JSON-Schema.".format(schema_file_name))
    exit(ERR_INVALID)
except ValidationError:
    # Just means that empty isn't valid, but the Schema itself is
    pass

configs = []
for f in config_file_names:
    config = None
    with open(f, "r") as data:
        try:
            config = safe_load(data)
        except YAMLError as e:
            print("YAML error: {} {}.".format(e.problem, e.problem_mark))
            print("\nYAML file '{}' is invalid.".format(f))
            exit(ERR_SYNTAX)
    if config is None:
        print("YAML file '{}' is empty.".format(f))
    configs.append(config)

assert len(configs) == len(config_file_names), "Not all configurations loaded."

### SCHEMA VALIDATION
#
#
for c, f in zip(configs, config_file_names):
    try:
        validate(instance=c, schema=schema)
    except ValidationError as e:
        print(e)
        print("\nConfig file '{}' does not validate in schema.".format(f))
        exit(ERR_INVALID)