From 63eb7303e9d3a0d488e5e28e84e0c0d3f9432b1e Mon Sep 17 00:00:00 2001 From: Christian Kellner Date: Wed, 10 Feb 2021 17:39:15 +0000 Subject: [PATCH] meta: support format version 2 for module infos When loading the schema information via the source code of a module, look for a `SCHEMA_2` global variable, representing the schema version 2. Extend the `get_schema` method so in takes a `version` keyword argument. Rework the code so that if version 2 for the format is specified but no dedicated schema data is found, a fallback based on the version 1 is provided. This makes it easy to use all existing stages without explicitly duplicating all schema information. NB: The code is not very pretty, the hope is that in the future, the module, being an executable, could be called with a command line switch, a la `--schema ` and this would return the schema data. So that hackery code we currently have will hopefully vanish soon. I am sorry though for this mess. --- osbuild/meta.py | 51 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/osbuild/meta.py b/osbuild/meta.py index f05bc4fd..a26d7fcb 100644 --- a/osbuild/meta.py +++ b/osbuild/meta.py @@ -287,42 +287,60 @@ class ModuleInfo: self.desc = info["desc"] self.opts = info["schema"] - def get_schema(self): + def _load_opts(self, version, fallback=None): + raw = self.opts[version] + if not raw and fallback: + raw = self.opts[fallback] + if not raw: + raise ValueError(f"Unsupported version: {version}") + + return json.loads("{" + raw + "}") + + def _make_options(self, version): + if version == "2": + raw = self.opts["2"] + if not raw: + return self._make_options("1") + elif version == "1": + raw = '"options": {' + self.opts["1"] + '}' + else: + raise ValueError(f"Unsupported version: {version}") + + return json.loads("{" + raw + "}") + + def get_schema(self, version="1"): schema = { "title": f"Pipeline {self.type}", "type": "object", "additionalProperties": False, } - raw = self.opts["1"] - opts = json.loads("{" + raw + "}") - if self.type in ("Stage", "Assembler"): + type_id = "type" if version == "2" else "name" + opts = self._make_options(version) schema["properties"] = { - "name": {"enum": [self.name]}, - "options": { - "type": "object", - **opts - } + type_id: {"enum": [self.name]}, + **opts, } - schema["required"] = ["name"] + schema["required"] = [type_id] else: + opts = self._load_opts(version, "1") schema.update(opts) # if there are is a definitions node, it needs to be at # the top level schema node, since the schema inside the # stages is written as-if they were the root node and # so are the references - definitions = opts.get("definitions") - if definitions: - schema["definitions"] = definitions - del schema["properties"]["options"]["definitions"] + options = schema.get("properties", {}).get("options", {}) + if "definitions" in options: + schema["definitions"] = options["definitions"] + del options["definitions"] return schema @classmethod def load(cls, root, klass, name) -> Optional["ModuleInfo"]: - names = ['SCHEMA'] + names = ["SCHEMA", "SCHEMA_2"] def value(a): v = a.value @@ -359,7 +377,8 @@ class ModuleInfo: info = { 'schema': { - "1": values.get("SCHEMA", "") + "1": values.get("SCHEMA", ""), + "2": values.get("SCHEMA_2") }, 'desc': doclist[0], 'info': "\n".join(doclist[1:])