Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve interrupt handling and send signal to current loop #149

Merged
merged 1 commit into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 30 additions & 13 deletions qubesbuilder/cli/cli_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"""
QubesBuilder command-line interface - base module.
"""

import asyncio
import signal
import sys
import traceback
from typing import Callable, List
Expand Down Expand Up @@ -61,21 +62,37 @@
self.list_commands = self.list_commands_for_help # type: ignore

def __call__(self, *args, **kwargs):
loop = asyncio.get_event_loop()

def _handle_interrupt():
tasks = asyncio.all_tasks(loop)
for task in tasks:
task.cancel()

Check warning on line 70 in qubesbuilder/cli/cli_base.py

View check run for this annotation

Codecov / codecov/patch

qubesbuilder/cli/cli_base.py#L68-L70

Added lines #L68 - L70 were not covered by tests

loop.add_signal_handler(signal.SIGINT, _handle_interrupt)

rc = 1
try:
return self.main(*args, **kwargs)
rv = self.main(*args, standalone_mode=False, **kwargs)
if isinstance(rv, list) and set(rv) == {None}:
rc = 0
except Exception as exc:
QubesBuilderLogger.error(f"An error occurred: {str(exc)}")
if self.debug is True:
formatted_traceback = "".join(traceback.format_exception(exc))
QubesBuilderLogger.error(
"\n" + formatted_traceback.rstrip("\n")
)

if isinstance(exc, click.ClickException):
# pylint: disable=no-member
sys.exit(exc.exit_code)
if isinstance(exc, click.Abort) or "Signals.SIGINT" in str(exc):
QubesBuilderLogger.warning(f"Interrupting...")

Check warning on line 81 in qubesbuilder/cli/cli_base.py

View check run for this annotation

Codecov / codecov/patch

qubesbuilder/cli/cli_base.py#L80-L81

Added lines #L80 - L81 were not covered by tests
else:
sys.exit(1)
QubesBuilderLogger.error(f"An error occurred: {str(exc)}")
if self.debug is True:
formatted_traceback = "".join(

Check warning on line 85 in qubesbuilder/cli/cli_base.py

View check run for this annotation

Codecov / codecov/patch

qubesbuilder/cli/cli_base.py#L83-L85

Added lines #L83 - L85 were not covered by tests
traceback.format_exception(exc)
)
QubesBuilderLogger.error(

Check warning on line 88 in qubesbuilder/cli/cli_base.py

View check run for this annotation

Codecov / codecov/patch

qubesbuilder/cli/cli_base.py#L88

Added line #L88 was not covered by tests
"\n" + formatted_traceback.rstrip("\n")
)
if isinstance(exc, click.ClickException):

Check warning on line 91 in qubesbuilder/cli/cli_base.py

View check run for this annotation

Codecov / codecov/patch

qubesbuilder/cli/cli_base.py#L91

Added line #L91 was not covered by tests
# pylint: disable=no-member
rc = exc.exit_code

Check warning on line 93 in qubesbuilder/cli/cli_base.py

View check run for this annotation

Codecov / codecov/patch

qubesbuilder/cli/cli_base.py#L93

Added line #L93 was not covered by tests
finally:
sys.exit(rc)

def get_command(self, ctx, cmd_name):
rv = click.Group.get_command(self, ctx, cmd_name)
Expand Down
2 changes: 1 addition & 1 deletion qubesbuilder/executors/qubes.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ def run( # type: ignore
)
continue
raise e
except ExecutorError as e:
except (subprocess.CalledProcessError, ExecutorError) as e:
if dispvm and self._clean_on_error:
self.cleanup(dispvm)
raise e
Expand Down
34 changes: 33 additions & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def qb_call(builder_conf, artifacts_dir, *args, **kwargs):
f"artifacts-dir={artifacts_dir}",
*args,
]
subprocess.check_call(cmd, **kwargs)
return subprocess.check_call(cmd, **kwargs)


def qb_call_output(builder_conf, artifacts_dir, *args, **kwargs):
Expand Down Expand Up @@ -2567,3 +2567,35 @@ def test_installer_init_cache(artifacts_dir):
rpms = list(templates_cache.glob("*.rpm"))
assert rpms
assert rpms[0].name.startswith("qubes-template-debian-12-minimal-4.2.0")


def test_existent_command(artifacts_dir):
result = qb_call(
DEFAULT_BUILDER_CONF,
artifacts_dir,
"config",
"get-components",
"get-templates",
)
assert result == 0


def test_non_existent_command(artifacts_dir):
with pytest.raises(subprocess.CalledProcessError):
result = qb_call(
DEFAULT_BUILDER_CONF, artifacts_dir, "non-existent-command"
)
assert result == 2


def test_non_existent_component(artifacts_dir):
with pytest.raises(subprocess.CalledProcessError):
result = qb_call(
DEFAULT_BUILDER_CONF,
artifacts_dir,
"-c",
"non-existent-component",
"package",
"all",
)
assert result == 2