Work on changing the WaitingWidget to check for Docker

This commit is contained in:
Micah Lee 2021-11-22 14:06:31 -08:00
parent d1c33bfcf5
commit 42ce884419
No known key found for this signature in database
GPG key ID: 403C2657CD994F73
8 changed files with 83 additions and 158 deletions

6
.gitmodules vendored
View file

@ -1,6 +0,0 @@
[submodule "vendor/hyperkit"]
path = vendor/hyperkit
url = https://github.com/moby/hyperkit.git
[submodule "vendor/vpnkit"]
path = vendor/vpnkit
url = https://github.com/moby/vpnkit.git

View file

@ -35,8 +35,8 @@ class GlobalCommon(object):
# App data folder # App data folder
self.appdata_path = appdirs.user_config_dir("dangerzone") self.appdata_path = appdirs.user_config_dir("dangerzone")
# In case we have a custom container # Container name
self.custom_container = None self.container_name = "dangerzone.rocks/dangerzone"
# Languages supported by tesseract # Languages supported by tesseract
self.ocr_languages = { self.ocr_languages = {
@ -380,12 +380,6 @@ class GlobalCommon(object):
) )
print(Back.BLACK + Fore.YELLOW + Style.DIM + "╰──────────────────────────╯") print(Back.BLACK + Fore.YELLOW + Style.DIM + "╰──────────────────────────╯")
def get_container_name(self):
if self.custom_container:
return self.custom_container
else:
return "docker.io/flmcode/dangerzone"
def get_resource_path(self, filename): def get_resource_path(self, filename):
if getattr(sys, "dangerzone_dev", False): if getattr(sys, "dangerzone_dev", False):
# Look for resources directory relative to python file # Look for resources directory relative to python file
@ -414,17 +408,17 @@ class GlobalCommon(object):
def exec_dangerzone_container(self, input_filename, output_filename, ocr_lang): def exec_dangerzone_container(self, input_filename, output_filename, ocr_lang):
convert(self, input_filename, output_filename, ocr_lang) convert(self, input_filename, output_filename, ocr_lang)
args = [self.dz_container_path] + args # args = [self.dz_container_path] + args
args_str = " ".join(pipes.quote(s) for s in args) # args_str = " ".join(pipes.quote(s) for s in args)
print(Style.DIM + "> " + Style.NORMAL + Fore.CYAN + args_str) # print(Style.DIM + "> " + Style.NORMAL + Fore.CYAN + args_str)
# Execute dangerzone-container # # Execute dangerzone-container
return subprocess.Popen( # return subprocess.Popen(
args, # args,
startupinfo=self.get_subprocess_startupinfo(), # startupinfo=self.get_subprocess_startupinfo(),
stdout=subprocess.PIPE, # stdout=subprocess.PIPE,
stderr=subprocess.PIPE, # stderr=subprocess.PIPE,
) # )
def get_subprocess_startupinfo(self): def get_subprocess_startupinfo(self):
if platform.system() == "Windows": if platform.system() == "Windows":

View file

@ -9,12 +9,6 @@ from PySide2 import QtCore, QtWidgets
from .common import GuiCommon from .common import GuiCommon
from .main_window import MainWindow from .main_window import MainWindow
from .systray import SysTray from .systray import SysTray
from .docker_installer import (
is_docker_installed,
is_docker_ready,
DockerInstaller,
AuthorizationFailed,
)
from ..global_common import GlobalCommon from ..global_common import GlobalCommon
@ -86,15 +80,6 @@ def gui_main(filename):
# Allow Ctrl-C to smoothly quit the program instead of throwing an exception # Allow Ctrl-C to smoothly quit the program instead of throwing an exception
signal.signal(signal.SIGINT, signal.SIG_DFL) signal.signal(signal.SIGINT, signal.SIG_DFL)
# See if we need to install Docker (Windows-only)
if (platform.system() == "Windows" or platform.system() == "Darwin") and (
not is_docker_installed() or not is_docker_ready(global_common)
):
click.echo("Docker is either not installed or not running")
docker_installer = DockerInstaller(gui_common)
docker_installer.start()
return
# Create the system tray # Create the system tray
systray = SysTray(global_common, gui_common, app, app_wrapper) systray = SysTray(global_common, gui_common, app, app_wrapper)

View file

@ -14,7 +14,6 @@ elif platform.system() == "Linux":
import getpass import getpass
from xdg.DesktopEntry import DesktopEntry from xdg.DesktopEntry import DesktopEntry
from .docker_installer import is_docker_ready
from ..settings import Settings from ..settings import Settings
@ -36,7 +35,7 @@ class GuiCommon(object):
# Preload list of PDF viewers on computer # Preload list of PDF viewers on computer
self.pdf_viewers = self._find_pdf_viewers() self.pdf_viewers = self._find_pdf_viewers()
# Are we done waiting (for VM to start, or container to install) # Are we done waiting (for Docker Desktop to be installed, or for container to install)
self.is_waiting_finished = False self.is_waiting_finished = False
def get_window_icon(self): def get_window_icon(self):

View file

@ -1,99 +0,0 @@
import os
import subprocess
import shutil
import platform
from PySide2 import QtCore, QtWidgets
class AuthorizationFailed(Exception):
pass
container_runtime = shutil.which("docker")
def is_docker_installed():
return container_runtime is not None
def is_docker_ready(global_common):
# Run `docker image ls` without an error
with subprocess.Popen([container_runtime, "image", "ls"]) as p:
outs, errs = p.communicate()
# The user canceled, or permission denied
if p.returncode == 126 or p.returncode == 127:
raise AuthorizationFailed
# Return true if it succeeds
if p.returncode == 0:
return True
else:
print(outs)
print(errs)
return False
def launch_docker_windows(global_common):
docker_desktop_path = "C:\\Program Files\\Docker\\Docker\\Docker Desktop.exe"
subprocess.Popen(
[docker_desktop_path], startupinfo=global_common.get_subprocess_startupinfo()
)
class DockerInstaller(QtWidgets.QDialog):
def __init__(self, gui_common):
super(DockerInstaller, self).__init__()
self.setWindowTitle("Dangerzone")
self.setWindowIcon(gui_common.get_window_icon())
# self.setMinimumHeight(170)
label = QtWidgets.QLabel()
if platform.system() == "Darwin":
label.setText("Dangerzone for macOS requires Docker")
elif platform.system() == "Windows":
label.setText("Dangerzone for Windows requires Docker")
label.setStyleSheet("QLabel { font-weight: bold; }")
label.setAlignment(QtCore.Qt.AlignCenter)
self.task_label = QtWidgets.QLabel()
self.task_label.setAlignment(QtCore.Qt.AlignCenter)
self.task_label.setWordWrap(True)
self.task_label.setOpenExternalLinks(True)
self.ok_button = QtWidgets.QPushButton("OK")
self.ok_button.clicked.connect(self.ok_clicked)
buttons_layout = QtWidgets.QHBoxLayout()
buttons_layout.addStretch()
buttons_layout.addWidget(self.ok_button)
buttons_layout.addStretch()
layout = QtWidgets.QVBoxLayout()
layout.addWidget(label)
layout.addWidget(self.task_label)
layout.addLayout(buttons_layout)
layout.addStretch()
self.setLayout(layout)
if platform.system() == "Darwin":
self.docker_path = "/Applications/Docker.app/Contents/Resources/bin/docker"
elif platform.system() == "Windows":
self.docker_path = shutil.which("docker.exe")
def ok_clicked(self):
self.accept()
def start(self):
if not os.path.exists(self.docker_path):
self.task_label.setText(
"<a href='https://www.docker.com/products/docker-desktop'>Download Docker Desktop</a>, install it, and then run Dangerzone again."
)
self.task_label.setTextFormat(QtCore.Qt.RichText)
else:
self.task_label.setText(
"Docker Desktop is installed, but you must launch it first. Open Docker, make sure it's running, and then open Dangerzone again."
)
return self.exec_() == QtWidgets.QDialog.Accepted

View file

@ -3,6 +3,7 @@ import platform
import tempfile import tempfile
import subprocess import subprocess
import json import json
import shutil
from PySide2 import QtCore, QtGui, QtWidgets from PySide2 import QtCore, QtGui, QtWidgets
from colorama import Style, Fore from colorama import Style, Fore
@ -43,7 +44,7 @@ class MainWindow(QtWidgets.QMainWindow):
header_layout.addWidget(header_label) header_layout.addWidget(header_label)
header_layout.addStretch() header_layout.addStretch()
# Waiting widget, replaces content widget while VM is booting # Waiting widget, replaces content widget while container runtime isn't available
self.waiting_widget = WaitingWidget(self.global_common, self.gui_common) self.waiting_widget = WaitingWidget(self.global_common, self.gui_common)
self.waiting_widget.finished.connect(self.waiting_finished) self.waiting_widget.finished.connect(self.waiting_finished)
@ -53,7 +54,7 @@ class MainWindow(QtWidgets.QMainWindow):
) )
self.content_widget.close_window.connect(self.close) self.content_widget.close_window.connect(self.close)
# Only use the waiting widget if we have a VM # Only use the waiting widget if container runtime isn't available
if self.gui_common.is_waiting_finished: if self.gui_common.is_waiting_finished:
self.waiting_widget.hide() self.waiting_widget.hide()
self.content_widget.show() self.content_widget.show()
@ -101,6 +102,15 @@ class InstallContainerThread(QtCore.QThread):
class WaitingWidget(QtWidgets.QWidget): class WaitingWidget(QtWidgets.QWidget):
# These are the possible states that the WaitingWidget can show.
#
# Windows and macOS states:
# - "not_installed"
# - "not_running"
# - "install_container"
#
# Linux states
# - "install_container"
finished = QtCore.Signal() finished = QtCore.Signal()
def __init__(self, global_common, gui_common): def __init__(self, global_common, gui_common):
@ -110,34 +120,78 @@ class WaitingWidget(QtWidgets.QWidget):
self.label = QtWidgets.QLabel() self.label = QtWidgets.QLabel()
self.label.setAlignment(QtCore.Qt.AlignCenter) self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setTextFormat(QtCore.Qt.RichText)
self.label.setStyleSheet("QLabel { font-size: 20px; }") self.label.setStyleSheet("QLabel { font-size: 20px; }")
# Buttons
check_button = QtWidgets.QPushButton("Check Again")
check_button.clicked.connect(self.check_state)
buttons_layout = QtWidgets.QHBoxLayout()
buttons_layout.addStretch()
buttons_layout.addWidget(check_button)
buttons_layout.addStretch()
self.buttons = QtWidgets.QWidget()
self.buttons.setLayout(buttons_layout)
# Layout # Layout
layout = QtWidgets.QVBoxLayout() layout = QtWidgets.QVBoxLayout()
layout.addStretch() layout.addStretch()
layout.addWidget(self.label) layout.addWidget(self.label)
layout.addWidget(self.buttons)
layout.addStretch() layout.addStretch()
self.setLayout(layout) self.setLayout(layout)
if platform.system() == "Darwin": # Check the state
self.label.setText("Waiting for the Dangerzone virtual machine to start...") self.check_state()
self.global_common.vm.vm_state_change.connect(self.vm_state_change)
elif platform.system() == "Linux": def check_state(self):
self.label.setText("Installing the Dangerzone container...") state = None
# Can we find the container runtime binary binary
if platform.system() == "Linux":
container_runtime = shutil.which("podman")
else:
container_runtime = shutil.which("docker")
if container_runtime is None:
print("Docker is not installed")
state = "not_installed"
else:
# Can we run `docker image ls` without an error
with subprocess.Popen([container_runtime, "image", "ls"]) as p:
p.communicate()
if p.returncode != 0:
print("Docker is not running")
state = "not_running"
else:
# Always try installing the container
print("Ensuring the container is installed")
state = "install_container"
# Update the state
self.state_change(state)
def state_change(self, state):
if state == "not_installed":
self.label.setText(
"<strong>Dangerzone requires Docker</strong><br><br><a href='https://www.docker.com/products/docker-desktop'>Download Docker Desktop</a> and install it."
)
self.buttons.show()
elif state == "not_running":
self.label.setText(
"Docker Desktop is installed, but you must launch it first. Open Docker and make sure it's running in the background."
)
self.buttons.show()
else:
self.label.setText(
"Installing the Dangerzone container..."
)
self.buttons.hide()
self.install_container_t = InstallContainerThread(self.global_common) self.install_container_t = InstallContainerThread(self.global_common)
self.install_container_t.finished.connect(self.finished) self.install_container_t.finished.connect(self.finished)
self.install_container_t.start() self.install_container_t.start()
else:
self.label.setText("Platform not implemented yet")
def vm_state_change(self, state):
if state == self.global_common.vm.STATE_ON:
self.finished.emit()
elif state == self.global_common.vm.STATE_FAIL:
self.label.setText("Dangerzone virtual machine failed to start :(")
class ContentWidget(QtWidgets.QWidget): class ContentWidget(QtWidgets.QWidget):
close_window = QtCore.Signal() close_window = QtCore.Signal()

1
vendor/hyperkit vendored

@ -1 +0,0 @@
Subproject commit 09fe9202a29a56a532d07505fd91831989b9afeb

1
vendor/vpnkit vendored

@ -1 +0,0 @@
Subproject commit 16ed722e6dc24307e99aee931ffd0eb80a9487d0