Fix wrong container runtime detection on Linux

Use "podman" when on Linux, and "docker" otherwise.
Show what the error was when the command failed.
This commit is contained in:
Alexis Métaireau 2024-08-30 17:12:28 +02:00 committed by Alex Pyrgiotis
parent 9b9e265b11
commit a014fee3e5
No known key found for this signature in database
GPG key ID: B6C15EBA0357C9AA
3 changed files with 75 additions and 17 deletions

View file

@ -5,7 +5,7 @@ import platform
import signal import signal
import sys import sys
import typing import typing
from typing import Dict, List, Optional from typing import List, Optional
import click import click
import colorama import colorama

View file

@ -10,14 +10,18 @@ from typing import List, Optional
# FIXME: See https://github.com/freedomofpress/dangerzone/issues/320 for more details. # FIXME: See https://github.com/freedomofpress/dangerzone/issues/320 for more details.
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from PySide2 import QtCore, QtGui, QtSvg, QtWidgets from PySide2 import QtCore, QtGui, QtSvg, QtWidgets
from PySide2.QtWidgets import QAction from PySide2.QtCore import Qt
from PySide2.QtWidgets import QAction, QTextEdit
else: else:
try: try:
from PySide6 import QtCore, QtGui, QtSvg, QtWidgets from PySide6 import QtCore, QtGui, QtSvg, QtWidgets
from PySide6.QtCore import Qt
from PySide6.QtGui import QAction from PySide6.QtGui import QAction
from PySide6.QtWidgets import QTextEdit
except ImportError: except ImportError:
from PySide2 import QtCore, QtGui, QtSvg, QtWidgets from PySide2 import QtCore, QtGui, QtSvg, QtWidgets
from PySide2.QtWidgets import QAction from PySide2.QtWidgets import QAction, QTextEdit
from PySide2.QtCore import Qt
from .. import errors from .. import errors
from ..document import SAFE_EXTENSION, Document from ..document import SAFE_EXTENSION, Document
@ -434,10 +438,29 @@ class WaitingWidgetContainer(WaitingWidget):
self.buttons = QtWidgets.QWidget() self.buttons = QtWidgets.QWidget()
self.buttons.setLayout(buttons_layout) self.buttons.setLayout(buttons_layout)
# Error
self.error_text = QTextEdit()
self.error_text.setReadOnly(True)
self.error_text.setStyleSheet(
"""
QTextEdit {
font-family: Consolas, Monospace;
font-size: 12px;
background-color: #fff;
color: #000;
padding: 10px;
}
"""
)
self.error_text.setVisible(False)
# Enable copying
self.error_text.setTextInteractionFlags(Qt.TextSelectableByMouse)
# Layout # Layout
layout = QtWidgets.QVBoxLayout() layout = QtWidgets.QVBoxLayout()
layout.addStretch() layout.addStretch()
layout.addWidget(self.label) layout.addWidget(self.label)
layout.addWidget(self.error_text)
layout.addStretch() layout.addStretch()
layout.addWidget(self.buttons) layout.addWidget(self.buttons)
layout.addStretch() layout.addStretch()
@ -448,49 +471,84 @@ class WaitingWidgetContainer(WaitingWidget):
def check_state(self) -> None: def check_state(self) -> None:
state: Optional[str] = None state: Optional[str] = None
error: Optional[str] = None
try: try:
if isinstance( # Sanity check if isinstance( # Sanity check
self.dangerzone.isolation_provider, Container self.dangerzone.isolation_provider, Container
): ):
container_runtime = self.dangerzone.isolation_provider.get_runtime() container_runtime = self.dangerzone.isolation_provider.get_runtime()
runtime_name = self.dangerzone.isolation_provider.get_runtime_name()
except NoContainerTechException as e: except NoContainerTechException as e:
log.error(str(e)) log.error(str(e))
state = "not_installed" state = "not_installed"
else: else:
# Can we run `docker image ls` without an error # Can we run `docker/podman image ls` without an error
with subprocess.Popen( with subprocess.Popen(
[container_runtime, "image", "ls"], [container_runtime, "image", "ls"],
stdout=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL, stderr=subprocess.PIPE,
startupinfo=get_subprocess_startupinfo(), startupinfo=get_subprocess_startupinfo(),
) as p: ) as p:
p.communicate() _, stderr = p.communicate()
if p.returncode != 0: if p.returncode != 0:
log.error("Docker is not running") log.error(f"{runtime_name} is not running")
state = "not_running" state = "not_running"
error = stderr.decode()
else: else:
# Always try installing the container # Always try installing the container
state = "install_container" state = "install_container"
# Update the state # Update the state
self.state_change(state) self.state_change(state, error)
def state_change(self, state: str) -> None: def state_change(self, state: str, error: str | None = None) -> None:
if state == "not_installed": if state == "not_installed":
self.label.setText( if platform.system() == "Linux":
"<strong>Dangerzone Requires Docker Desktop</strong><br><br><a href='https://www.docker.com/products/docker-desktop'>Download Docker Desktop</a>, install it, and open it." self.label.setText(
) (
"<strong>Dangerzone requires Podman</strong><br><br>"
"Install it and retry."
)
)
else:
self.label.setText(
(
"<strong>Dangerzone requires Docker Desktop</strong><br><br>"
"<a href='https://www.docker.com/products/docker-desktop'>Download Docker Desktop</a>"
", install it, and open it."
)
)
self.buttons.show() self.buttons.show()
elif state == "not_running": elif state == "not_running":
self.label.setText( if platform.system() == "Linux":
"<strong>Dangerzone Requires Docker Desktop</strong><br><br>Docker is installed but isn't running.<br><br>Open Docker and make sure it's running in the background." # "not_running" here means that the `podman image ls` command failed.
) message = (
"<strong>Dangerzone requires Podman</strong><br><br>"
"Podman is installed but cannot run properly. See errors below"
)
if error:
self.error_text.setPlainText(error)
self.error_text.setVisible(True)
self.label.setText(message)
else:
self.label.setText(
(
"<strong>Dangerzone requires Docker Desktop</strong><br><br>"
"Docker is installed but isn't running.<br><br>"
"Open Docker and make sure it's running in the background."
)
)
self.buttons.show() self.buttons.show()
else: else:
self.label.setText( self.label.setText(
"Installing the Dangerzone container image.<br><br>This might take a few minutes..." (
"Installing the Dangerzone container image.<br><br>"
"This might take a few minutes..."
)
) )
self.buttons.hide() self.buttons.hide()
self.install_container_t = InstallContainerThread(self.dangerzone) self.install_container_t = InstallContainerThread(self.dangerzone)

View file

@ -242,7 +242,7 @@ class Container(IsolationProvider):
# `int`. # `int`.
# #
# See https://stackoverflow.com/a/37888668 # See https://stackoverflow.com/a/37888668
if not type(val) == _type: if type(val) is not _type:
raise ValueError("Status field has incorrect type") raise ValueError("Status field has incorrect type")
def parse_progress_trusted(self, document: Document, line: str) -> None: def parse_progress_trusted(self, document: Document, line: str) -> None: