mirror of
https://github.com/freedomofpress/dangerzone.git
synced 2025-04-28 18:02:38 +02:00
After downloading Docker, install it, launch it for the first time, and once it's ready, re-launch dangerzone
This commit is contained in:
parent
abb56b68ac
commit
13aac3348a
2 changed files with 141 additions and 26 deletions
|
@ -4,10 +4,11 @@ import sys
|
|||
import signal
|
||||
import platform
|
||||
import click
|
||||
import time
|
||||
|
||||
from .common import Common
|
||||
from .main_window import MainWindow
|
||||
from .docker_installer import is_docker_installed, DockerInstaller
|
||||
from .docker_installer import is_docker_installed, is_docker_ready, DockerInstaller
|
||||
|
||||
dangerzone_version = "0.1.0"
|
||||
|
||||
|
@ -31,8 +32,19 @@ def main(filename):
|
|||
if platform.system() == "Darwin" and not is_docker_installed(common):
|
||||
print("Docker is not installed!")
|
||||
docker_installer = DockerInstaller(common)
|
||||
if docker_installer.launch():
|
||||
main(filename)
|
||||
if docker_installer.start():
|
||||
# When installer finished, wait up to 20 minutes for the user to launch it
|
||||
for i in range(120):
|
||||
if is_docker_installed(common) and is_docker_ready(common):
|
||||
main(filename)
|
||||
return
|
||||
|
||||
print("Waiting for docker to be available ...")
|
||||
time.sleep(1)
|
||||
|
||||
# Give up
|
||||
print("Docker not available, giving up")
|
||||
|
||||
return
|
||||
|
||||
# Main window
|
||||
|
|
|
@ -2,11 +2,14 @@ import os
|
|||
import stat
|
||||
import requests
|
||||
import tempfile
|
||||
import subprocess
|
||||
import shutil
|
||||
import time
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
def is_docker_installed(common):
|
||||
# Soes the docker binary exist?
|
||||
# Does the docker binary exist?
|
||||
if os.path.isdir("/Applications/Docker.app") and os.path.exists(
|
||||
common.container_runtime
|
||||
):
|
||||
|
@ -16,6 +19,15 @@ def is_docker_installed(common):
|
|||
return False
|
||||
|
||||
|
||||
def is_docker_ready(common):
|
||||
# Run `docker ps` without an error
|
||||
try:
|
||||
subprocess.run([common.container_runtime, "ps"], check=True)
|
||||
return True
|
||||
except subprocess.CalledProcessError:
|
||||
return False
|
||||
|
||||
|
||||
class DockerInstaller(QtWidgets.QDialog):
|
||||
def __init__(self, common):
|
||||
super(DockerInstaller, self).__init__()
|
||||
|
@ -26,8 +38,10 @@ class DockerInstaller(QtWidgets.QDialog):
|
|||
|
||||
label = QtWidgets.QLabel("Dangerzone for macOS 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.progress = QtWidgets.QProgressBar()
|
||||
self.progress.setMinimum(0)
|
||||
|
@ -40,14 +54,14 @@ class DockerInstaller(QtWidgets.QDialog):
|
|||
self.launch_button.setStyleSheet("QPushButton { font-weight: bold; }")
|
||||
self.launch_button.clicked.connect(self.launch_clicked)
|
||||
self.launch_button.hide()
|
||||
cancel_button = QtWidgets.QPushButton("Cancel")
|
||||
cancel_button.clicked.connect(self.cancel_clicked)
|
||||
self.cancel_button = QtWidgets.QPushButton("Cancel")
|
||||
self.cancel_button.clicked.connect(self.cancel_clicked)
|
||||
|
||||
buttons_layout = QtWidgets.QHBoxLayout()
|
||||
buttons_layout.addStretch()
|
||||
buttons_layout.addWidget(self.install_button)
|
||||
buttons_layout.addWidget(self.launch_button)
|
||||
buttons_layout.addWidget(cancel_button)
|
||||
buttons_layout.addWidget(self.cancel_button)
|
||||
buttons_layout.addStretch()
|
||||
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
|
@ -55,29 +69,27 @@ class DockerInstaller(QtWidgets.QDialog):
|
|||
layout.addWidget(self.task_label)
|
||||
layout.addWidget(self.progress)
|
||||
layout.addLayout(buttons_layout)
|
||||
layout.addStretch()
|
||||
self.setLayout(layout)
|
||||
|
||||
self.tmp_dir = tempfile.TemporaryDirectory(prefix="/tmp/dangerzone-docker-")
|
||||
self.dmg_filename = os.path.join(self.tmp_dir.name, "Docker.dmg")
|
||||
|
||||
# Threads
|
||||
self.download_t = None
|
||||
|
||||
def cancel_clicked(self):
|
||||
if self.download_t:
|
||||
self.download_t.terminate()
|
||||
self.reject()
|
||||
|
||||
def install_clicked(self):
|
||||
print("Install clicked")
|
||||
|
||||
def launch_clicked(self):
|
||||
print("Launch clicked")
|
||||
self.install_t = None
|
||||
|
||||
def update_progress(self, value, maximum):
|
||||
self.progress.setMaximum(maximum)
|
||||
self.progress.setValue(value)
|
||||
|
||||
def update_task_label(self, s):
|
||||
self.task_label.setText(s)
|
||||
|
||||
def download_finished(self):
|
||||
self.task_label.setText("Finished downloading Docker")
|
||||
self.download_t = None
|
||||
self.progress.hide()
|
||||
self.install_button.show()
|
||||
|
||||
def download_failed(self, status_code):
|
||||
|
@ -86,17 +98,72 @@ class DockerInstaller(QtWidgets.QDialog):
|
|||
|
||||
def download(self):
|
||||
self.task_label.setText("Downloading Docker")
|
||||
self.download_t = Downloader()
|
||||
|
||||
self.timer = QtCore.QTimer()
|
||||
self.timer.timeout.connect(self.start_download)
|
||||
self.timer.setSingleShot(True)
|
||||
self.timer.start(10)
|
||||
|
||||
def start_download(self):
|
||||
self.download_t = Downloader(self.dmg_filename)
|
||||
self.download_t.download_finished.connect(self.download_finished)
|
||||
self.download_t.download_failed.connect(self.download_failed)
|
||||
self.download_t.update_progress.connect(self.update_progress)
|
||||
self.download_t.start()
|
||||
|
||||
def install(self):
|
||||
pass
|
||||
def install_finished(self):
|
||||
self.task_label.setText("Finished installing Docker")
|
||||
self.install_t = None
|
||||
self.progress.hide()
|
||||
self.install_button.hide()
|
||||
self.launch_button.show()
|
||||
self.cancel_button.setEnabled(True)
|
||||
|
||||
def launch(self):
|
||||
self.download()
|
||||
def install_failed(self, exception):
|
||||
print(f"Install failed: {exception}")
|
||||
self.task_label.setText(f"Install failed: {exception}")
|
||||
self.install_t = None
|
||||
self.cancel_button.setEnabled(True)
|
||||
|
||||
def install_clicked(self):
|
||||
self.task_label.setText("Installing Docker")
|
||||
self.install_button.hide()
|
||||
self.cancel_button.setEnabled(False)
|
||||
|
||||
self.progress.setMinimum(0)
|
||||
self.progress.setMaximum(0)
|
||||
|
||||
self.timer = QtCore.QTimer()
|
||||
self.timer.timeout.connect(self.start_installer)
|
||||
self.timer.setSingleShot(True)
|
||||
self.timer.start(10)
|
||||
|
||||
def start_installer(self):
|
||||
self.install_t = Installer(self.dmg_filename)
|
||||
self.install_t.install_finished.connect(self.install_finished)
|
||||
self.install_t.install_failed.connect(self.install_failed)
|
||||
self.install_t.update_task_label.connect(self.update_task_label)
|
||||
self.install_t.start()
|
||||
|
||||
def launch_clicked(self):
|
||||
print("Launching Docker")
|
||||
self.accept()
|
||||
subprocess.Popen(["open", "-a", "Docker.app"])
|
||||
|
||||
def cancel_clicked(self):
|
||||
if self.download_t:
|
||||
self.download_t.terminate()
|
||||
if self.install_t:
|
||||
self.install_t.terminate()
|
||||
self.reject()
|
||||
|
||||
def start(self):
|
||||
if not os.path.isdir("/Applications/Docker.app"):
|
||||
self.download()
|
||||
else:
|
||||
self.task_label.setText("Docker is installed, but you must launch it first")
|
||||
self.progress.hide()
|
||||
self.launch_button.show()
|
||||
return self.exec_() == QtWidgets.QDialog.Accepted
|
||||
|
||||
|
||||
|
@ -105,10 +172,9 @@ class Downloader(QtCore.QThread):
|
|||
download_failed = QtCore.pyqtSignal(int)
|
||||
update_progress = QtCore.pyqtSignal(int, int)
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, dmg_filename):
|
||||
super(Downloader, self).__init__()
|
||||
self.tmp_dir = tempfile.TemporaryDirectory(prefix="/tmp/dangerzone-docker-")
|
||||
self.dmg_filename = os.path.join(self.tmp_dir.name, "Docker.dmg")
|
||||
self.dmg_filename = dmg_filename
|
||||
|
||||
def run(self):
|
||||
print(f"Downloading docker to {self.dmg_filename}")
|
||||
|
@ -130,3 +196,40 @@ class Downloader(QtCore.QThread):
|
|||
|
||||
self.download_finished.emit()
|
||||
|
||||
|
||||
class Installer(QtCore.QThread):
|
||||
install_finished = QtCore.pyqtSignal()
|
||||
install_failed = QtCore.pyqtSignal(str)
|
||||
update_task_label = QtCore.pyqtSignal(str)
|
||||
|
||||
def __init__(self, dmg_filename):
|
||||
super(Installer, self).__init__()
|
||||
self.dmg_filename = dmg_filename
|
||||
|
||||
def run(self):
|
||||
print(f"Installing Docker")
|
||||
try:
|
||||
# Mount the dmg
|
||||
self.update_task_label.emit(f"Mounting Docker.dmg")
|
||||
subprocess.run(["hdiutil", "attach", "-nobrowse", self.dmg_filename])
|
||||
|
||||
# Copy Docker.app to Applications
|
||||
self.update_task_label.emit("Copying Docker into Applications")
|
||||
shutil.copytree("/Volumes/Docker/Docker.app", "/Applications/Docker.app")
|
||||
|
||||
# Sync
|
||||
self.update_task_label.emit("Syncing filesystem")
|
||||
subprocess.run(["sync"])
|
||||
|
||||
# Wait, to prevent early crash
|
||||
time.sleep(1)
|
||||
|
||||
# Unmount the dmg
|
||||
self.update_task_label.emit(f"Unmounting /Volumes/Docker")
|
||||
subprocess.run(["hdiutil", "detach", "/Volumes/Docker"])
|
||||
|
||||
self.install_finished.emit()
|
||||
|
||||
except Exception as e:
|
||||
self.install_failed.emit(str(e))
|
||||
return
|
||||
|
|
Loading…
Reference in a new issue