Extend the interface of the isolation provider

Add the following two methods in the isolation provider:
1. `.is_available()`: Mainly used for the Container isolation provider,
   it specifies whether the container runtime is up and running. May be
   used in the future by other similar providers.
2. `.should_wait_install()`: Whether the isolation provider takes a
   while to be installed. Should be `True` only for the Container
   isolation provider, for the time being.
This commit is contained in:
Alex Pyrgiotis 2024-12-04 16:50:27 +02:00
parent e54567b7d4
commit 25fba42022
No known key found for this signature in database
GPG key ID: B6C15EBA0357C9AA
7 changed files with 38 additions and 22 deletions

View file

@ -26,12 +26,10 @@ else:
from .. import errors
from ..document import SAFE_EXTENSION, Document
from ..isolation_provider.container import (
Container,
NoContainerTechException,
NotAvailableContainerTechException,
)
from ..isolation_provider.dummy import Dummy
from ..isolation_provider.qubes import Qubes, is_qubes_native_conversion
from ..isolation_provider.qubes import is_qubes_native_conversion
from ..util import format_exception, get_resource_path, get_version
from .logic import Alert, CollapsibleBox, DangerzoneGui, UpdateDialog
from .updater import UpdateReport
@ -197,14 +195,11 @@ class MainWindow(QtWidgets.QMainWindow):
header_layout.addWidget(self.hamburger_button)
header_layout.addSpacing(15)
if isinstance(self.dangerzone.isolation_provider, Container):
if self.dangerzone.isolation_provider.should_wait_install():
# Waiting widget replaces content widget while container runtime isn't available
self.waiting_widget: WaitingWidget = WaitingWidgetContainer(self.dangerzone)
self.waiting_widget.finished.connect(self.waiting_finished)
elif isinstance(self.dangerzone.isolation_provider, Dummy) or isinstance(
self.dangerzone.isolation_provider, Qubes
):
else:
# Don't wait with dummy converter and on Qubes.
self.waiting_widget = WaitingWidget()
self.dangerzone.is_waiting_finished = True
@ -500,8 +495,7 @@ class WaitingWidgetContainer(WaitingWidget):
error: Optional[str] = None
try:
assert isinstance(self.dangerzone.isolation_provider, (Dummy, Container))
self.dangerzone.isolation_provider.is_runtime_available()
self.dangerzone.isolation_provider.is_available()
except NoContainerTechException as e:
log.error(str(e))
state = "not_installed"

View file

@ -254,6 +254,16 @@ class IsolationProvider(ABC):
)
return errors.exception_from_error_code(error_code)
@abstractmethod
def should_wait_install(self) -> bool:
"""Whether this isolation provider takes a lot of time to install."""
pass
@abstractmethod
def is_available(self) -> bool:
"""Whether the backing implementation of the isolation provider is available."""
pass
@abstractmethod
def get_max_parallel_conversions(self) -> int:
pass

View file

@ -304,7 +304,11 @@ class Container(IsolationProvider):
return True
@staticmethod
def is_runtime_available() -> bool:
def should_wait_install() -> bool:
return True
@staticmethod
def is_available() -> bool:
container_runtime = Container.get_runtime()
runtime_name = Container.get_runtime_name()
# Can we run `docker/podman image ls` without an error

View file

@ -40,9 +40,13 @@ class Dummy(IsolationProvider):
return True
@staticmethod
def is_runtime_available() -> bool:
def is_available() -> bool:
return True
@staticmethod
def should_wait_install() -> bool:
return False
def start_doc_to_pixels_proc(self, document: Document) -> subprocess.Popen:
cmd = [
sys.executable,

View file

@ -21,6 +21,14 @@ class Qubes(IsolationProvider):
def install(self) -> bool:
return True
@staticmethod
def is_available() -> bool:
return True
@staticmethod
def should_wait_install() -> bool:
return False
def get_max_parallel_conversions(self) -> int:
return 1

View file

@ -512,7 +512,7 @@ def test_not_available_container_tech_exception(
# Setup
mock_app = mocker.MagicMock()
dummy = Dummy()
fn = mocker.patch.object(dummy, "is_runtime_available")
fn = mocker.patch.object(dummy, "is_available")
fn.side_effect = NotAvailableContainerTechException(
"podman", "podman image ls logs"
)
@ -536,7 +536,7 @@ def test_no_container_tech_exception(qtbot: QtBot, mocker: MockerFixture) -> Non
dummy = mocker.MagicMock()
# Raise
dummy.is_runtime_available.side_effect = NoContainerTechException("podman")
dummy.is_available.side_effect = NoContainerTechException("podman")
dz = DangerzoneGui(mock_app, dummy)
widget = WaitingWidgetContainer(dz)

View file

@ -27,9 +27,7 @@ def provider() -> Container:
class TestContainer(IsolationProviderTest):
def test_is_runtime_available_raises(
self, provider: Container, fp: FakeProcess
) -> None:
def test_is_available_raises(self, provider: Container, fp: FakeProcess) -> None:
"""
NotAvailableContainerTechException should be raised when
the "podman image ls" command fails.
@ -40,18 +38,16 @@ class TestContainer(IsolationProviderTest):
stderr="podman image ls logs",
)
with pytest.raises(NotAvailableContainerTechException):
provider.is_runtime_available()
provider.is_available()
def test_is_runtime_available_works(
self, provider: Container, fp: FakeProcess
) -> None:
def test_is_available_works(self, provider: Container, fp: FakeProcess) -> None:
"""
No exception should be raised when the "podman image ls" can return properly.
"""
fp.register_subprocess(
[provider.get_runtime(), "image", "ls"],
)
provider.is_runtime_available()
provider.is_available()
def test_install_raise_if_image_cant_be_installed(
self, mocker: MockerFixture, provider: Container, fp: FakeProcess