In macOS, download Docker Desktop for the user if it is not installed

This commit is contained in:
Micah Lee 2020-02-06 17:11:46 -08:00
parent 70889646be
commit abb56b68ac
No known key found for this signature in database
GPG key ID: 403C2657CD994F73
6 changed files with 209 additions and 31 deletions

View file

@ -10,6 +10,7 @@ appdirs = "*"
pyxdg = {version = "*",platform_system = "== 'Linux'"} pyxdg = {version = "*",platform_system = "== 'Linux'"}
pyobjc-core = {version = "*",platform_system = "== 'Darwin'"} pyobjc-core = {version = "*",platform_system = "== 'Darwin'"}
pyobjc-framework-launchservices = {version = "*",platform_system = "== 'Darwin'"} pyobjc-framework-launchservices = {version = "*",platform_system = "== 'Darwin'"}
requests = "*"
[dev-packages] [dev-packages]
black = "*" black = "*"

94
Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "0ec85e528a50d2e61151ca453c5d6fc217b1695fee0673e86db6eea60491de02" "sha256": "695116394343f7849640651aff1c6957762111bbe2905e3e4d4473be4d6dfb43"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -24,6 +24,20 @@
"index": "pypi", "index": "pypi",
"version": "==1.4.3" "version": "==1.4.3"
}, },
"certifi": {
"hashes": [
"sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
"sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"
],
"version": "==2019.11.28"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"click": { "click": {
"hashes": [ "hashes": [
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
@ -32,6 +46,13 @@
"index": "pypi", "index": "pypi",
"version": "==7.0" "version": "==7.0"
}, },
"idna": {
"hashes": [
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
],
"version": "==2.8"
},
"pyobjc-core": { "pyobjc-core": {
"hashes": [ "hashes": [
"sha256:1a0fbf012fb575e0adf8c18cfd4453e657cc2c0deb2660c529bf524ba4c9149a", "sha256:1a0fbf012fb575e0adf8c18cfd4453e657cc2c0deb2660c529bf524ba4c9149a",
@ -81,36 +102,36 @@
}, },
"pyqt5": { "pyqt5": {
"hashes": [ "hashes": [
"sha256:2b79209aa6e4688f6ac46e6d2694236dcf91db5f3a87270150d0f82082e3d360", "sha256:2d94ec761fb656707050c68b41958e3a9f755bb1df96c064470f4096d2899e32",
"sha256:2f230f2dbd767099de7a0cb915abdf0cbc3256a0b5bb910eb09b99117db7a65b", "sha256:2f230f2dbd767099de7a0cb915abdf0cbc3256a0b5bb910eb09b99117db7a65b",
"sha256:3d6e315e6e2d6489a2e1e0148d00e784e277c6590c189227d6060f15b9be690a", "sha256:31b142a868152d60c6323e0527edb692fdf05fd7cb4fe2fe9ce07d1ce560221a",
"sha256:812233bd155735377e2e9c7eea7a28815f357440334db51788d941e2a8b62f64", "sha256:713b9a201f5e7b2fca8691373e5d5c8c2552a51d87ca9ffbb1461e34e3241211",
"sha256:be10fa95e6bdc9cad616ebf368c51b3f5748138b2b3a600cf7c4f80b78cb9852" "sha256:a0bfe9fd718bca4de3e33000347e048f73126b6dc46530eb020b0251a638ee9d"
], ],
"index": "pypi", "index": "pypi",
"version": "==5.14.1" "version": "==5.14.1"
}, },
"pyqt5-sip": { "pyqt5-sip": {
"hashes": [ "hashes": [
"sha256:02d94786bada670ab17a2b62ce95b3cf8e3b40c99d36007593a6334d551840bb", "sha256:1115728644bbadcde5fc8a16e7918bd31915a42dd6fb36b10d4afb78c582753e",
"sha256:06bc66b50556fb949f14875a4c224423dbf03f972497ccb883fb19b7b7c3b346", "sha256:1f4289276d355b6521dc2cc956189315da6f13adfb6bbab8f25ebd15e3bce1d4",
"sha256:091fbbe10a7aebadc0e8897a9449cda08d3c3f663460d812eca3001ca1ed3526", "sha256:288c6dc18a8d6a20981c07b715b5695d9b66880778565f3792bc6e38f14f20fb",
"sha256:0a067ade558befe4d46335b13d8b602b5044363bfd601419b556d4ec659bca18", "sha256:3f665376d9e52faa9855c3736a66ce6d825f85c86d7774d3c393f09da23f4f86",
"sha256:1910c1cb5a388d4e59ebb2895d7015f360f3f6eeb1700e7e33e866c53137eb9e", "sha256:6b4860c4305980db509415d0af802f111d15f92016c9422eb753bc8883463456",
"sha256:1c7ad791ec86247f35243bbbdd29cd59989afbe0ab678e0a41211f4407f21dd8", "sha256:7ffa39763097f64de129cf5cc770a651c3f65d2466b4fe05bef2bd2efbaa38e6",
"sha256:3c330ff1f70b3eaa6f63dce9274df996dffea82ad9726aa8e3d6cbe38e986b2f", "sha256:8a18e6f45d482ddfe381789979d09ee13aa6450caa3a0476503891bccb3ac709",
"sha256:482a910fa73ee0e36c258d7646ef38f8061774bbc1765a7da68c65056b573341", "sha256:8da842d3d7bf8931d1093105fb92702276b6dbb7e801abbaaa869405d616171a",
"sha256:7695dfafb4f5549ce1290ae643d6508dfc2646a9003c989218be3ce42a1aa422", "sha256:b42021229424aa44e99b3b49520b799fd64ff6ae8b53f79f903bbd85719a28e4",
"sha256:8274ed50f4ffbe91d0f4cc5454394631edfecd75dc327aa01be8bc5818a57e88", "sha256:b5b4906445fe980aee76f20400116b6904bf5f30d0767489c13370e42a764020",
"sha256:9047d887d97663790d811ac4e0d2e895f1bf2ecac4041691487de40c30239480", "sha256:c1e730a9eb2ec3869ed5d81b0f99f6e2460fb4d77750444c0ec183b771d798f7",
"sha256:9f6ab1417ecfa6c1ce6ce941e0cebc03e3ec9cd9925058043229a5f003ae5e40", "sha256:cbeeae6b45234a1654657f79943f8bccd3d14b4e7496746c62cf6fbce69442c7",
"sha256:b43ba2f18999d41c3df72f590348152e14cd4f6dcea2058c734d688dfb1ec61f", "sha256:d46b0f8effc554de52a1466b1bd80e5cb4bce635a75ac4e7ad6247c965dec5b9",
"sha256:c3ab9ea1bc3f4ce8c57ebc66fb25cd044ef92ed1ca2afa3729854ecc59658905", "sha256:e28c3abc9b62a1b7e796891648b9f14f8167b31c8e7990fae79654777252bb4d",
"sha256:da69ba17f6ece9a85617743cb19de689f2d63025bf8001e2facee2ec9bcff18f", "sha256:e6078f5ee7d31c102910d0c277a110e1c2a20a3fc88cd017a39e170120586d3f",
"sha256:ef3c7a0bf78674b0dda86ff5809d8495019903a096c128e1f160984b37848f73", "sha256:ee1a12f09d5af2304273bfd2f6b43835c1467d5ed501a6c95f5405637fa7750a",
"sha256:fabff832046643cdb93920ddaa8f77344df90768930fbe6bb33d211c4dcd0b5e" "sha256:f314f31f5fd39b06897f013f425137e511d45967150eb4e424a363d8138521c6"
], ],
"version": "==12.7.0" "version": "==12.7.1"
}, },
"pyxdg": { "pyxdg": {
"hashes": [ "hashes": [
@ -120,15 +141,30 @@
"index": "pypi", "index": "pypi",
"markers": "platform_system == 'Linux'", "markers": "platform_system == 'Linux'",
"version": "==0.26" "version": "==0.26"
},
"requests": {
"hashes": [
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
],
"index": "pypi",
"version": "==2.22.0"
},
"urllib3": {
"hashes": [
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
],
"version": "==1.25.8"
} }
}, },
"develop": { "develop": {
"altgraph": { "altgraph": {
"hashes": [ "hashes": [
"sha256:d6814989f242b2b43025cba7161fc1b8fb487a62cd49c49245d6fd01c18ac997", "sha256:1f05a47122542f97028caf78775a095fbe6a2699b5089de8477eb583167d69aa",
"sha256:ddf5320017147ba7b810198e0b6619bd7b5563aa034da388cea8546b877f9b0c" "sha256:c623e5f3408ca61d4016f23a681b9adb100802ca3e3da5e718915a9e4052cebe"
], ],
"version": "==0.16.1" "version": "==0.17"
}, },
"appdirs": { "appdirs": {
"hashes": [ "hashes": [
@ -163,10 +199,10 @@
}, },
"macholib": { "macholib": {
"hashes": [ "hashes": [
"sha256:b71afea242d5ad4caacbdb79d80e75815d033fbc30f45954b2f3397f39683fd6", "sha256:0c436bc847e7b1d9bda0560351bf76d7caf930fb585a828d13608839ef42c432",
"sha256:c72bda118afe7799570fcb4114315d5c9c5416e48eacf1198da39b4d77201559" "sha256:c500f02867515e6c60a27875b408920d18332ddf96b4035ef03beddd782d4281"
], ],
"version": "==1.13" "version": "==1.14"
}, },
"pathspec": { "pathspec": {
"hashes": [ "hashes": [

View file

@ -2,10 +2,12 @@ from PyQt5 import QtCore, QtWidgets
import os import os
import sys import sys
import signal import signal
import platform
import click import click
from .common import Common from .common import Common
from .main_window import MainWindow from .main_window import MainWindow
from .docker_installer import is_docker_installed, DockerInstaller
dangerzone_version = "0.1.0" dangerzone_version = "0.1.0"
@ -25,6 +27,14 @@ def main(filename):
# Common object # Common object
common = Common(app) common = Common(app)
# See if we need to install Docker...
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)
return
# Main window # Main window
main_window = MainWindow(common) main_window = MainWindow(common)

View file

@ -50,7 +50,7 @@ class Common(object):
# Container runtime # Container runtime
if platform.system() == "Darwin": if platform.system() == "Darwin":
self.container_runtime = "docker" self.container_runtime = "/usr/local/bin/docker"
else: else:
self.container_runtime = "podman" self.container_runtime = "podman"

View file

@ -0,0 +1,132 @@
import os
import stat
import requests
import tempfile
from PyQt5 import QtCore, QtGui, QtWidgets
def is_docker_installed(common):
# Soes the docker binary exist?
if os.path.isdir("/Applications/Docker.app") and os.path.exists(
common.container_runtime
):
# Is it executable?
st = os.stat(common.container_runtime)
return bool(st.st_mode & stat.S_IXOTH)
return False
class DockerInstaller(QtWidgets.QDialog):
def __init__(self, common):
super(DockerInstaller, self).__init__()
self.common = common
self.setWindowTitle("dangerzone")
self.setWindowIcon(QtGui.QIcon(self.common.get_resource_path("logo.png")))
label = QtWidgets.QLabel("Dangerzone for macOS requires Docker")
label.setStyleSheet("QLabel { font-weight: bold; }")
self.task_label = QtWidgets.QLabel()
self.progress = QtWidgets.QProgressBar()
self.progress.setMinimum(0)
self.install_button = QtWidgets.QPushButton("Install Docker")
self.install_button.setStyleSheet("QPushButton { font-weight: bold; }")
self.install_button.clicked.connect(self.install_clicked)
self.install_button.hide()
self.launch_button = QtWidgets.QPushButton("Launch Docker")
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)
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.addStretch()
layout = QtWidgets.QVBoxLayout()
layout.addWidget(label)
layout.addWidget(self.task_label)
layout.addWidget(self.progress)
layout.addLayout(buttons_layout)
self.setLayout(layout)
# 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")
def update_progress(self, value, maximum):
self.progress.setMaximum(maximum)
self.progress.setValue(value)
def download_finished(self):
self.task_label.setText("Finished downloading Docker")
self.download_t = None
self.install_button.show()
def download_failed(self, status_code):
print(f"Download failed: status code {status_code}")
self.download_t = None
def download(self):
self.task_label.setText("Downloading Docker")
self.download_t = Downloader()
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 launch(self):
self.download()
return self.exec_() == QtWidgets.QDialog.Accepted
class Downloader(QtCore.QThread):
download_finished = QtCore.pyqtSignal()
download_failed = QtCore.pyqtSignal(int)
update_progress = QtCore.pyqtSignal(int, int)
def __init__(self):
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")
def run(self):
print(f"Downloading docker to {self.dmg_filename}")
with requests.get(
"https://download.docker.com/mac/stable/Docker.dmg", stream=True
) as r:
if r.status_code != 200:
self.download_failed.emit(r.status_code)
return
total_bytes = int(r.headers.get("content-length"))
downloaded_bytes = 0
with open(self.dmg_filename, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
if chunk: # filter out keep-alive new chunks
downloaded_bytes += f.write(chunk)
self.update_progress.emit(downloaded_bytes, total_bytes)
self.download_finished.emit()

View file

@ -1,6 +1,5 @@
import subprocess import subprocess
import time import time
import tempfile
import os import os
import pipes import pipes
import platform import platform