blob: 5ba89450fee476dc964741bb209c548deca04ddb [file] [log] [blame]
Brad Bishopc04b3f42020-05-01 08:17:59 -04001#!/usr/bin/env python3
2# SPDX-License-Identifier: Apache-2.0
3"""
4A tool for validating entity manager configurations.
5"""
6import argparse
7import json
Brad Bishopc04b3f42020-05-01 08:17:59 -04008import os
9import sys
10
Patrick Williamsfa8ee872022-12-07 07:00:42 -060011import jsonschema.validators
12
Brad Bishopc04b3f42020-05-01 08:17:59 -040013DEFAULT_SCHEMA_FILENAME = "global.json"
14
15
16def main():
17 parser = argparse.ArgumentParser(
18 description="Entity manager configuration validator",
19 )
20 parser.add_argument(
Patrick Williamsfa8ee872022-12-07 07:00:42 -060021 "-s",
22 "--schema",
23 help=(
Brad Bishopc04b3f42020-05-01 08:17:59 -040024 "Use the specified schema file instead of the default "
Patrick Williamsfa8ee872022-12-07 07:00:42 -060025 "(__file__/../../schemas/global.json)"
26 ),
27 )
Brad Bishopc04b3f42020-05-01 08:17:59 -040028 parser.add_argument(
Patrick Williamsfa8ee872022-12-07 07:00:42 -060029 "-c",
30 "--config",
31 action="append",
32 help=(
Brad Bishopc04b3f42020-05-01 08:17:59 -040033 "Validate the specified configuration files (can be "
34 "specified more than once) instead of the default "
Patrick Williamsfa8ee872022-12-07 07:00:42 -060035 "(__file__/../../configurations/**.json)"
36 ),
37 )
Brad Bishopc04b3f42020-05-01 08:17:59 -040038 parser.add_argument(
Patrick Williamsfa8ee872022-12-07 07:00:42 -060039 "-e",
40 "--expected-fails",
41 help=(
Brad Bishopc04b3f42020-05-01 08:17:59 -040042 "A file with a list of configurations to ignore should "
Patrick Williamsfa8ee872022-12-07 07:00:42 -060043 "they fail to validate"
44 ),
45 )
Brad Bishopc04b3f42020-05-01 08:17:59 -040046 parser.add_argument(
Patrick Williamsfa8ee872022-12-07 07:00:42 -060047 "-k",
48 "--continue",
49 action="store_true",
50 help="keep validating after a failure",
51 )
Brad Bishopc04b3f42020-05-01 08:17:59 -040052 parser.add_argument(
Patrick Williamsfa8ee872022-12-07 07:00:42 -060053 "-v", "--verbose", action="store_true", help="be noisy"
54 )
Brad Bishopc04b3f42020-05-01 08:17:59 -040055 args = parser.parse_args()
56
57 schema_file = args.schema
58 if schema_file is None:
59 try:
60 source_dir = os.path.realpath(__file__).split(os.sep)[:-2]
61 schema_file = os.sep + os.path.join(
Patrick Williamsfa8ee872022-12-07 07:00:42 -060062 *source_dir, "schemas", DEFAULT_SCHEMA_FILENAME
63 )
Patrick Williamscad2d1f2022-12-04 14:38:16 -060064 except Exception:
Brad Bishopc04b3f42020-05-01 08:17:59 -040065 sys.stderr.write(
66 "Could not guess location of {}\n".format(
Patrick Williamsfa8ee872022-12-07 07:00:42 -060067 DEFAULT_SCHEMA_FILENAME
68 )
69 )
Brad Bishopc04b3f42020-05-01 08:17:59 -040070 sys.exit(2)
71
72 schema = {}
73 try:
74 with open(schema_file) as fd:
75 schema = json.load(fd)
Patrick Williamscad2d1f2022-12-04 14:38:16 -060076 except FileNotFoundError:
Brad Bishopc04b3f42020-05-01 08:17:59 -040077 sys.stderr.write(
Patrick Williamsfa8ee872022-12-07 07:00:42 -060078 "Could not read schema file '{}'\n".format(schema_file)
79 )
Brad Bishopc04b3f42020-05-01 08:17:59 -040080 sys.exit(2)
81
82 config_files = args.config or []
83 if len(config_files) == 0:
84 try:
85 source_dir = os.path.realpath(__file__).split(os.sep)[:-2]
Patrick Williamsfa8ee872022-12-07 07:00:42 -060086 configs_dir = os.sep + os.path.join(*source_dir, "configurations")
Brad Bishopc04b3f42020-05-01 08:17:59 -040087 data = os.walk(configs_dir)
88 for root, _, files in data:
89 for f in files:
Patrick Williamsfa8ee872022-12-07 07:00:42 -060090 if f.endswith(".json"):
Brad Bishopc04b3f42020-05-01 08:17:59 -040091 config_files.append(os.path.join(root, f))
Patrick Williamscad2d1f2022-12-04 14:38:16 -060092 except Exception:
Patrick Williamsfa8ee872022-12-07 07:00:42 -060093 sys.stderr.write("Could not guess location of configurations\n")
Brad Bishopc04b3f42020-05-01 08:17:59 -040094 sys.exit(2)
95
96 configs = []
97 for config_file in config_files:
98 try:
99 with open(config_file) as fd:
100 configs.append(json.load(fd))
Patrick Williamscad2d1f2022-12-04 14:38:16 -0600101 except FileNotFoundError:
Brad Bishopc04b3f42020-05-01 08:17:59 -0400102 sys.stderr.write(
Patrick Williamsfa8ee872022-12-07 07:00:42 -0600103 "Could not parse config file '{}'\n".format(config_file)
104 )
Brad Bishopc04b3f42020-05-01 08:17:59 -0400105 sys.exit(2)
106
107 expected_fails = []
108 if args.expected_fails:
109 try:
110 with open(args.expected_fails) as fd:
111 for line in fd:
112 expected_fails.append(line.strip())
Patrick Williamscad2d1f2022-12-04 14:38:16 -0600113 except Exception:
Brad Bishopc04b3f42020-05-01 08:17:59 -0400114 sys.stderr.write(
Patrick Williamsfa8ee872022-12-07 07:00:42 -0600115 "Could not read expected fails file '{}'\n".format(
116 args.expected_fails
117 )
118 )
Brad Bishopc04b3f42020-05-01 08:17:59 -0400119 sys.exit(2)
120
121 base_uri = "file://{}/".format(
Patrick Williamsfa8ee872022-12-07 07:00:42 -0600122 os.path.split(os.path.realpath(schema_file))[0]
123 )
Brad Bishopc04b3f42020-05-01 08:17:59 -0400124 resolver = jsonschema.RefResolver(base_uri, schema)
125 validator = jsonschema.Draft7Validator(schema, resolver=resolver)
126
127 results = {
128 "invalid": [],
129 "unexpected_pass": [],
130 }
131 for config_file, config in zip(config_files, configs):
132 name = os.path.split(config_file)[1]
133 expect_fail = name in expected_fails
134 try:
135 validator.validate(config)
136 if expect_fail:
137 results["unexpected_pass"].append(name)
138 if not getattr(args, "continue"):
139 break
140 except jsonschema.exceptions.ValidationError as e:
141 if not expect_fail:
142 results["invalid"].append(name)
143 if args.verbose:
144 print(e)
145 if expect_fail or getattr(args, "continue"):
146 continue
147 break
148
149 exit_status = 0
150 if len(results["invalid"]) + len(results["unexpected_pass"]):
151 exit_status = 1
152 unexpected_pass_suffix = " **"
153 show_suffix_explanation = False
154 print("results:")
155 for f in config_files:
156 if any([x in f for x in results["unexpected_pass"]]):
157 show_suffix_explanation = True
158 print(" '{}' passed!{}".format(f, unexpected_pass_suffix))
159 if any([x in f for x in results["invalid"]]):
160 print(" '{}' failed!".format(f))
161
162 if show_suffix_explanation:
163 print("\n** configuration expected to fail")
164
165 sys.exit(exit_status)
166
167
168if __name__ == "__main__":
169 main()