diff --git a/inputs/org.osbuild.tree b/inputs/org.osbuild.tree index 69999c50..f5be2f83 100755 --- a/inputs/org.osbuild.tree +++ b/inputs/org.osbuild.tree @@ -2,8 +2,10 @@ """ Tree inputs -Resolve the given pipeline `id` to a path and return that. If -`id` is `null` or the empty string it returns an empty tree. +Open the tree produced by the pipeline supplied via the +first and only entry in `references`. The tree is opened +in read only mode. If the id is `null` or the empty +string it returns an empty tree. """ @@ -15,30 +17,53 @@ from osbuild.objectstore import StoreClient SCHEMA = """ "additionalProperties": false, -"required": ["pipeline"], +"required": ["origin", "references"], "properties": { - "pipeline": { - "description": "The Pipeline that built the desired tree", - "type": "object", - "required": ["id"], - "additionalProperties": false, - "properties": { - "id": { - "description": "Identifier for the pipeline", + "origin": { + "description": "The origin of the input (must be 'org.osbuild.pipeline')", + "type": "string", + "enum": ["org.osbuild.pipeline"] + }, + "references": { + "description": "Exactly one pipeline identifier to ues as tree input", + "oneOf": [{ + "type": "array", + "additionalItems": false, + "items": [{ "type": "string" - } - } + }] + }, { + "type": "object", + "additionalProperties": false, + "patternProperties": { + ".*": { + "type": "object", + "additionalProperties": false + } + }, + "minProperties": 1, + "maxProperties": 1 + }] } } """ +def error(msg): + json.dump({"error": msg}, sys.stdout) + sys.exit(1) + + def main(): args = json.load(sys.stdin) - options = args["options"] + refs = args["refs"] + + # input verification *must* have been done via schema + # verification. It is expected that origin is a pipeline + # and we have exactly one reference, i.e. a pipeline id + pid, _ = refs.popitem() store = StoreClient(connect_to=args["api"]["store"]) - pid = options["pipeline"]["id"] if not pid: path = store.mkdtemp(prefix="empty") @@ -46,8 +71,7 @@ def main(): path = store.read_tree(pid) if not path: - json.dump({"error": "Could find target"}, sys.stdout) - return 1 + error(f"Could not find pipeline with id '{pid}'") json.dump({"path": path}, sys.stdout) return 0 diff --git a/osbuild/formats/v1.py b/osbuild/formats/v1.py index 5a0644c6..1cf34e59 100644 --- a/osbuild/formats/v1.py +++ b/osbuild/formats/v1.py @@ -64,10 +64,9 @@ def load_assembler(description: Dict, index: Index, manifest: Manifest): stage = pipeline.add_stage(info, options, {}) info = index.get_module_info("Input", "org.osbuild.tree") - stage.inputs = { - "tree": Input(info, {"pipeline": {"id": base}}) - } - + ip = Input(info, "org.osbuild.pipeline", {}) + ip.add_reference(base) + stage.inputs = {"tree": ip} return pipeline diff --git a/osbuild/inputs.py b/osbuild/inputs.py index 7503de34..12900445 100644 --- a/osbuild/inputs.py +++ b/osbuild/inputs.py @@ -23,7 +23,7 @@ import json import os import subprocess -from typing import Dict, Tuple +from typing import Dict, Optional, Tuple from .meta import ModuleInfo from .objectstore import StoreServer @@ -34,14 +34,22 @@ class Input: A single input with its corresponding options. """ - def __init__(self, info: ModuleInfo, options: Dict): + def __init__(self, info: ModuleInfo, origin: str, options: Dict): self.info = info + self.origin = origin + self.refs = {} self.options = options or {} self.id = self.calc_id() + def add_reference(self, ref, options: Optional[Dict] = None): + self.refs[ref] = options or {} + self.id = self.calc_id() + def calc_id(self): m = hashlib.sha256() m.update(json.dumps(self.name, sort_keys=True).encode()) + m.update(json.dumps(self.origin, sort_keys=True).encode()) + m.update(json.dumps(self.refs, sort_keys=True).encode()) m.update(json.dumps(self.options, sort_keys=True).encode()) return m.hexdigest() @@ -52,8 +60,14 @@ class Input: def run(self, storeapi: StoreServer) -> Tuple[str, Dict]: name = self.info.name msg = { + # mandatory bits + "origin": self.origin, + "refs": self.refs, + + # global options "options": self.options, - "origin": name, + + # API endpoints "api": { "store": storeapi.socket_address }