From b5eac929d31907bf8383fb5fdd72df81099e9be0 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Mon, 5 Aug 2024 12:35:44 +0200 Subject: [PATCH] monitor: include `build_result` in jsonseq monitor streaming In order to avoid having to rely on the output of `osbuild --json` when using `--progress=JSONSeqMonitor` the monitor needs to include the `osbuild.pipeline.BuildResult` for each individual stage. This commit adds those to the montior. --- osbuild/monitor.py | 32 ++++++++++++++++++++++++-------- test/mod/test_monitor.py | 8 +++++++- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/osbuild/monitor.py b/osbuild/monitor.py index eebc4cc19..520c603ff 100644 --- a/osbuild/monitor.py +++ b/osbuild/monitor.py @@ -163,9 +163,12 @@ def as_dict(self): return d -def log_entry(message: Optional[str] = None, - context: Optional[Context] = None, - progress: Optional[Progress] = None) -> dict: +def log_entry( + message: Optional[str] = None, + context: Optional[Context] = None, + progress: Optional[Progress] = None, + build_result: Optional[osbuild.pipeline.BuildResult] = None, +) -> dict: """ Create a single log entry dict with a given message, context, and progress objects. All arguments are optional. A timestamp is added to the message. @@ -174,6 +177,7 @@ def log_entry(message: Optional[str] = None, # monitors support that return omitempty({ "message": message, + "build_result": build_result.as_dict() if build_result else None, "context": context.as_dict() if context else None, "progress": progress.as_dict() if progress else None, "timestamp": time.time(), @@ -254,7 +258,7 @@ def __init__(self, fd: int, total_steps: int = 0): super().__init__(fd, total_steps) self.timer_start = 0 - def result(self, result): + def result(self, result: osbuild.pipeline.BuildResult): duration = int(time.time() - self.timer_start) self.out.write(f"\n⏱ Duration: {duration}s\n") @@ -339,13 +343,25 @@ def result(self, result: osbuild.pipeline.BuildResult): # we may need to check pipeline ids here in the future if self._progress.sub_progress: self._progress.sub_progress.incr() - self.log(f"Finished module {result.name}", origin="osbuild.monitor") + + self._jsonseq(log_entry( + f"Finished module {result.name}", + context=self._context.with_origin("osbuild.monitor"), + progress=self._progress, + # We should probably remove the "output" key from the result + # as it is redundant, each output already generates a "log()" + # message that is streamed to the client. + build_result=result, + )) def log(self, message, origin: Optional[str] = None): - entry = log_entry(message, self._context.with_origin(origin), self._progress) - self._jsonseq(entry) + self._jsonseq(log_entry( + message, + context=self._context.with_origin(origin), + progress=self._progress, + )) - def _jsonseq(self, entry): + def _jsonseq(self, entry: dict) -> None: # follow rfc7464 (application/json-seq) self.out.write("\x1e") json.dump(entry, self.out) diff --git a/test/mod/test_monitor.py b/test/mod/test_monitor.py index 7f60c3f53..ef3aaaf4d 100644 --- a/test/mod/test_monitor.py +++ b/test/mod/test_monitor.py @@ -208,7 +208,7 @@ def test_json_progress_monitor(): mon.log("pipeline 1 message 2") mon.log("pipeline 1 finished", origin="org.osbuild") mon.result(osbuild.pipeline.BuildResult( - fake_noop_stage, returncode=0, output="output", error=None)) + fake_noop_stage, returncode=0, output="some output", error=None)) mon.finish({"success": True, "name": "test-pipeline-first"}) mon.begin(manifest.pipelines["test-pipeline-second"]) mon.log("pipeline 2 starting", origin="org.osbuild") @@ -268,6 +268,12 @@ def test_json_progress_monitor(): logitem = json.loads(log[i]) assert logitem["message"] == "Finished module org.osbuild.noop" assert logitem["context"]["id"] == id_start_module + assert logitem["build_result"] == { + "id": fake_noop_stage.id, + "name": "org.osbuild.noop", + "output": "some output", + "success": True, + } i += 1 logitem = json.loads(log[i])