api: add exception endpoint
Create a new api endpoint called exception, that communicates exception backtraces separately back to osbuild, as opposed to dumping them into the normal log. Additionally, add a corresponding test to check that a call to api.exception correctly sets API.exception.
This commit is contained in:
parent
661e202e79
commit
5dc5ddcf29
3 changed files with 55 additions and 3 deletions
|
|
@ -1,9 +1,12 @@
|
|||
import abc
|
||||
import asyncio
|
||||
import contextlib
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import traceback
|
||||
import threading
|
||||
from typing import Dict, Optional
|
||||
from .util.types import PathLike
|
||||
|
|
@ -141,6 +144,7 @@ class API(BaseAPI):
|
|||
self._output_pipe = None
|
||||
self.monitor = monitor
|
||||
self.metadata = {}
|
||||
self.exception = None
|
||||
|
||||
@property
|
||||
def output(self):
|
||||
|
|
@ -182,9 +186,14 @@ class API(BaseAPI):
|
|||
fds.append(data.fileno())
|
||||
sock.send({"type": "fd", "fd": 0}, fds=fds)
|
||||
|
||||
def _get_exception(self, message):
|
||||
self.exception = message["exception"]
|
||||
|
||||
def _message(self, msg, fds, sock):
|
||||
if msg["method"] == 'add-metadata':
|
||||
self._set_metadata(msg)
|
||||
elif msg["method"] == 'exception':
|
||||
self._get_exception(msg)
|
||||
elif msg["method"] == 'get-arguments':
|
||||
self._get_arguments(sock)
|
||||
|
||||
|
|
@ -193,6 +202,29 @@ class API(BaseAPI):
|
|||
os.close(self._output_pipe)
|
||||
self._output_pipe = None
|
||||
|
||||
def exception(e, path="/run/osbuild/api/osbuild"):
|
||||
"""Send exception to osbuild"""
|
||||
traceback.print_exception(type(e), e, e.__traceback__, file=sys.stderr)
|
||||
with jsoncomm.Socket.new_client(path) as client:
|
||||
msg = {
|
||||
"method": "exception",
|
||||
"exception": {
|
||||
"type": str(type(e)),
|
||||
"value": str(e),
|
||||
"traceback": str(e.__traceback__)
|
||||
}
|
||||
}
|
||||
client.send(msg)
|
||||
|
||||
sys.exit(2)
|
||||
|
||||
# pylint: disable=broad-except
|
||||
@contextlib.contextmanager
|
||||
def exception_handler(path="/run/osbuild/api/osbuild"):
|
||||
try:
|
||||
yield
|
||||
except Exception as e:
|
||||
exception(e, path)
|
||||
|
||||
def arguments(path="/run/osbuild/api/osbuild"):
|
||||
"""Retrieve the input arguments that were supplied to API"""
|
||||
|
|
|
|||
|
|
@ -18,13 +18,14 @@ def cleanup(*objs):
|
|||
|
||||
|
||||
class BuildResult:
|
||||
def __init__(self, origin, returncode, output, metadata):
|
||||
def __init__(self, origin, returncode, output, metadata, error):
|
||||
self.name = origin.name
|
||||
self.id = origin.id
|
||||
self.options = origin.options
|
||||
self.success = returncode == 0
|
||||
self.output = output
|
||||
self.metadata = metadata
|
||||
self.error = error
|
||||
|
||||
def as_dict(self):
|
||||
return vars(self)
|
||||
|
|
@ -92,7 +93,7 @@ class Stage:
|
|||
binds=[os.fspath(tree) + ":/run/osbuild/tree"],
|
||||
readonly_binds=ro_binds)
|
||||
|
||||
return BuildResult(self, r.returncode, r.output, api.metadata)
|
||||
return BuildResult(self, r.returncode, r.output, api.metadata, api.exception)
|
||||
|
||||
|
||||
class Assembler:
|
||||
|
|
@ -151,7 +152,7 @@ class Assembler:
|
|||
binds=binds,
|
||||
readonly_binds=ro_binds)
|
||||
|
||||
return BuildResult(self, r.returncode, r.output, api.metadata)
|
||||
return BuildResult(self, r.returncode, r.output, api.metadata, api.exception)
|
||||
|
||||
|
||||
class Pipeline:
|
||||
|
|
|
|||
|
|
@ -79,6 +79,25 @@ class TestAPI(unittest.TestCase):
|
|||
self.assertEqual(data, args)
|
||||
|
||||
|
||||
def test_exception(self):
|
||||
# Check that 'api.exception' correctly sets 'API.exception'
|
||||
tmpdir = self.tmp.name
|
||||
path = os.path.join(tmpdir, "osbuild-api")
|
||||
args = {}
|
||||
monitor = osbuild.monitor.BaseMonitor(sys.stderr.fileno())
|
||||
|
||||
def exception(path):
|
||||
with osbuild.api.exception_handler(path):
|
||||
raise ValueError("osbuild test exception")
|
||||
|
||||
api = osbuild.api.API(args, monitor, socket_address=path)
|
||||
with api:
|
||||
p = mp.Process(target=exception, args=(path, ))
|
||||
p.start()
|
||||
p.join()
|
||||
self.assertIsNotNone(api.exception, "Exception not set")
|
||||
self.assertEqual(api.exception["value"], "osbuild test exception")
|
||||
|
||||
def test_metadata(self):
|
||||
# Check that `api.metadata` leads to `API.metadata` being
|
||||
# set correctly
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue