112 lines
4.6 KiB
Diff
112 lines
4.6 KiB
Diff
From 3d453d180d9b7bf0e283e313d1b5e294a5b4aeeb Mon Sep 17 00:00:00 2001
|
|
From: Roman Prilipskii <rprilipskii@cloudlinux.com>
|
|
Date: Wed, 9 Nov 2022 07:22:54 +0400
|
|
Subject: [PATCH 14/16] Expose tracebacks from actor exceptions
|
|
|
|
Previously when leapp would be running actors, any exceptions raised by
|
|
those actors in child processes would only be output to stderr,
|
|
while the main process would only have the exit code to examine.
|
|
|
|
Consequently, there was no real way to log occuring exceptions -
|
|
only providing the aforementioned exit code was possible.
|
|
|
|
This patch adds a pipe between the main and the actor processes,
|
|
through which a formatted exception + traceback string is provided to
|
|
the main process.
|
|
This string is then packaged into the LeappRuntimeError thrown from the
|
|
process, to be utilized downstream in
|
|
workflow/command code.
|
|
---
|
|
leapp/exceptions.py | 4 +++-
|
|
leapp/repository/actor_definition.py | 31 +++++++++++++++++++++-------
|
|
2 files changed, 27 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/leapp/exceptions.py b/leapp/exceptions.py
|
|
index 1bd4222..ef9d647 100644
|
|
--- a/leapp/exceptions.py
|
|
+++ b/leapp/exceptions.py
|
|
@@ -109,7 +109,9 @@ class UnknownCommandError(LeappError):
|
|
|
|
|
|
class LeappRuntimeError(LeappError):
|
|
- pass
|
|
+ def __init__(self, message, exception_info):
|
|
+ super().__init__(message)
|
|
+ self.exception_info = exception_info
|
|
|
|
|
|
class StopActorExecution(Exception):
|
|
diff --git a/leapp/repository/actor_definition.py b/leapp/repository/actor_definition.py
|
|
index 7c0beb9..2b5df1a 100644
|
|
--- a/leapp/repository/actor_definition.py
|
|
+++ b/leapp/repository/actor_definition.py
|
|
@@ -4,9 +4,10 @@ import logging
|
|
import os
|
|
import pkgutil
|
|
import sys
|
|
+import traceback
|
|
import warnings
|
|
from io import UnsupportedOperation
|
|
-from multiprocessing import Process, Queue
|
|
+from multiprocessing import Process, Queue, Pipe
|
|
|
|
import leapp.libraries.actor # noqa # pylint: disable=unused-import
|
|
from leapp.actors import get_actor_metadata, get_actors
|
|
@@ -56,7 +57,7 @@ class ActorCallContext(object):
|
|
self.skip_dialogs = skip_dialogs
|
|
|
|
@staticmethod
|
|
- def _do_run(stdin, logger, messaging, definition, config_model, skip_dialogs, args, kwargs):
|
|
+ def _do_run(stdin, logger, messaging, definition, config_model, skip_dialogs, error_pipe, args, kwargs):
|
|
if stdin is not None:
|
|
try:
|
|
sys.stdin = os.fdopen(stdin)
|
|
@@ -69,7 +70,13 @@ class ActorCallContext(object):
|
|
target_actor = [actor for actor in get_actors() if actor.name == definition.name][0]
|
|
actor_instance = target_actor(logger=logger, messaging=messaging, config_model=config_model,
|
|
skip_dialogs=skip_dialogs)
|
|
- actor_instance.run(*args, **kwargs)
|
|
+ try:
|
|
+ actor_instance.run(*args, **kwargs)
|
|
+ except Exception:
|
|
+ # Send the exception data string to the parent process
|
|
+ # and reraise.
|
|
+ error_pipe.send(traceback.format_exc())
|
|
+ raise
|
|
try:
|
|
# By this time this is no longer set, so we have to get it back
|
|
os.environ['LEAPP_CURRENT_ACTOR'] = actor_instance.name
|
|
@@ -96,15 +103,25 @@ class ActorCallContext(object):
|
|
stdin = sys.stdin.fileno()
|
|
except UnsupportedOperation:
|
|
stdin = None
|
|
+
|
|
+ pipe_receiver, pipe_sender = Pipe()
|
|
p = Process(target=self._do_run,
|
|
args=(stdin, self.logger, self.messaging, self.definition, self.config_model,
|
|
- self.skip_dialogs, args, kwargs))
|
|
+ self.skip_dialogs, pipe_sender, args, kwargs))
|
|
p.start()
|
|
p.join()
|
|
if p.exitcode != 0:
|
|
- raise LeappRuntimeError(
|
|
- 'Actor {actorname} unexpectedly terminated with exit code: {exitcode}'
|
|
- .format(actorname=self.definition.name, exitcode=p.exitcode))
|
|
+ err_message = "Actor {actorname} unexpectedly terminated with exit code: {exitcode}".format(
|
|
+ actorname=self.definition.name, exitcode=p.exitcode)
|
|
+
|
|
+ exception_info = None
|
|
+ # If there's data in the pipe, it's formatted exception info.
|
|
+ if pipe_receiver.poll():
|
|
+ exception_info = pipe_receiver.recv()
|
|
+
|
|
+ # This LeappRuntimeError will contain an exception traceback
|
|
+ # in addition to the above message.
|
|
+ raise LeappRuntimeError(err_message, exception_info)
|
|
|
|
|
|
class ActorDefinition(object):
|
|
--
|
|
2.39.0
|
|
|