From cab775f9c21aa6a2ff56cb28e4e5a357de7eb2c5 Mon Sep 17 00:00:00 2001 From: Jeff Morgan Date: Tue, 9 Sep 2014 16:23:48 -0700 Subject: [PATCH] Progress for default images and vbox --- meteor/client/lib/docker.js | 5 +- meteor/client/lib/installer.js | 77 ++++++++++------ meteor/client/lib/util.js | 40 ++++---- meteor/client/lib/virtualbox.js | 4 +- meteor/client/main.less | 1 + .../stylesheets/radial-progress.import.less | 91 +++++++++++++++++++ .../views/dashboard/setup/setup-install.html | 6 +- .../views/dashboard/setup/setup-install.js | 3 + .../views/includes/radial-progress.html | 17 ++++ package.json | 3 +- 10 files changed, 196 insertions(+), 51 deletions(-) create mode 100644 meteor/client/stylesheets/radial-progress.import.less create mode 100644 meteor/client/views/includes/radial-progress.html diff --git a/meteor/client/lib/docker.js b/meteor/client/lib/docker.js index e959079d08..4759ab0a53 100644 --- a/meteor/client/lib/docker.js +++ b/meteor/client/lib/docker.js @@ -6,6 +6,9 @@ var path = require('path'); Docker = {}; Docker.DOCKER_HOST = '192.168.60.103'; +Docker.DEFAULT_IMAGES_FILENAME = 'base-images-0.0.2.tar.gz'; +Docker.DEFAULT_IMAGES_CHECKSUM = '67e7c7562991a4208c90007461ec14bc184a52ad6048e6bed3e8a8c2b306cee7'; // Sha256 Checksum + Docker.client = function () { return new Dockerode({host: Docker.DOCKER_HOST, port: '2375'}); }; @@ -283,7 +286,7 @@ Docker.reloadDefaultContainers = function (callback) { return; } console.log('Loading new Kitematic default images.'); - docker.loadImage(path.join(Util.getBinDir(), 'base-images.tar.gz'), {}, function (err) { + docker.loadImage(path.join(Util.getResourceDir(), Docker.DEFAULT_IMAGES_FILENAME), {}, function (err) { if (err) { callback(err); return; diff --git a/meteor/client/lib/installer.js b/meteor/client/lib/installer.js index e99e9664f0..aa20d8bc52 100644 --- a/meteor/client/lib/installer.js +++ b/meteor/client/lib/installer.js @@ -22,26 +22,42 @@ Installer.isUpToDate = function () { */ Installer.steps = [ { - run: function (callback) { + run: function (callback, progressCallback) { var installed = VirtualBox.installed(); if (!installed) { - VirtualBox.install(function (err) { - callback(err); + Util.downloadFile(Installer.baseURL + VirtualBox.INSTALLER_FILENAME, path.join(Util.getResourceDir(), VirtualBox.INSTALLER_FILENAME), VirtualBox.INSTALLER_CHECKSUM, function (err) { + if (err) {callback(err); return;} + VirtualBox.install(function (err) { + if (!VirtualBox.installed()) { + callback('VirtualBox could not be installed. The installation either failed or was cancelled. Please try closing all VirtualBox instances and try again.'); + } else { + callback(err); + } + }); + }, function (progress) { + progressCallback(progress); }); } else { // Version 4.3.12 is required. VirtualBox.version(function (err, installedVersion) { - if (err) { - callback(err); - return; - } + if (err) {callback(err); return;} if (Util.compareVersions(installedVersion, VirtualBox.REQUIRED_VERSION) < 0) { - VirtualBox.install(function (err) { - if (Util.compareVersions(installedVersion, VirtualBox.REQUIRED_VERSION) < 0) { - callback('VirtualBox could not be installed. The installation either failed or was cancelled. Please try closing all VirtualBox instances and try again.'); - } else { - callback(err); - } + // Download a newer version of Virtualbox + Util.downloadFile(Installer.baseURL + VirtualBox.INSTALLER_FILENAME, path.join(Util.getResourceDir(), VirtualBox.INSTALLER_FILENAME), VirtualBox.INSTALLER_CHECKSUM, function (err) { + if (err) {callback(err); return;} + VirtualBox.install(function (err) { + if (err) {callback(err); return;} + VirtualBox.version(function (err, installedVersion) { + if (err) {callback(err); return;} + if (Util.compareVersions(installedVersion, VirtualBox.REQUIRED_VERSION) < 0) { + callback('VirtualBox could not be installed. The installation either failed or was cancelled. Please try closing all VirtualBox instances and try again.'); + } else { + callback(err); + } + }); + }, function (progress) { + progressCallback(progress); + }); }); } else { callback(); @@ -50,8 +66,8 @@ Installer.steps = [ } }, pastMessage: 'VirtualBox installed', - message: 'Installing VirtualBox', - futureMessage: 'Install VirtualBox if necessary' + message: 'Downloading & Installing VirtualBox', + futureMessage: 'Download & Install VirtualBox if necessary' }, // Initialize Boot2Docker if necessary. @@ -78,9 +94,9 @@ Installer.steps = [ } }); }, - pastMessage: 'Setup the Boot2Docker VM (if required)', - message: 'Setting up the Boot2Docker VM', - futureMessage: 'Set up the Boot2Docker VM(if required)' + pastMessage: 'Setup the Kitematic VM (if required)', + message: 'Setting up the Kitematic VM', + futureMessage: 'Set up the Kitematic VM(if required)' }, { @@ -89,9 +105,9 @@ Installer.steps = [ callback(err); }); }, - pastMessage: 'Added custom host adapter to the Boot2Docker VM', - message: 'Adding custom host adapter to the Boot2Docker VM', - futureMessage: 'Add custom host adapter to the Boot2Docker VM' + pastMessage: 'Added custom host adapter to the Kitematic VM', + message: 'Adding custom host adapter to the Kitematic VM', + futureMessage: 'Add custom host adapter to the Kitematic VM' }, // Start the Kitematic VM @@ -111,8 +127,8 @@ Installer.steps = [ } }); }, - pastMessage: 'Started the Boot2Docker VM', - message: 'Starting the Boot2Docker VM', + pastMessage: 'Started the Kitematic VM', + message: 'Starting the Kitematic VM', futureMessage: 'Start the Kitematic VM' }, @@ -129,12 +145,16 @@ Installer.steps = [ // Set up the default Kitematic images { - run: function (callback) { - Docker.reloadDefaultContainers(function (err) { - callback(err); + run: function (callback, progressCallback) { + Util.downloadFile(Installer.baseURL + Docker.DEFAULT_IMAGES_FILENAME, path.join(Util.getResourceDir(), Docker.DEFAULT_IMAGES_FILENAME), Docker.DEFAULT_IMAGES_CHECKSUM, function (err) { + Docker.reloadDefaultContainers(function (err) { + callback(err); + }); + }, function (progress) { + progressCallback(progress); }); }, - pastMessage: 'Started the Boot2Docker VM', + pastMessage: 'Set up the default Kitematic images.', message: 'Setting up the default Kitematic images...', subMessage: '(This may take a few minutes)', futureMessage: 'Set up the default Kitematic images' @@ -147,6 +167,7 @@ Installer.run = function (callback) { Session.set('numberOfInstallSteps', this.steps.length); async.eachSeries(this.steps, function (step, callback) { console.log('Performing step ' + currentStep); + Session.set('currentInstallStepProgress', 0); step.run(function (err) { if (err) { callback(err); @@ -155,6 +176,8 @@ Installer.run = function (callback) { Session.set('currentInstallStep', currentStep); callback(); } + }, function (progress) { + Session.set('currentInstallStepProgress', progress); }); }, function (err) { if (err) { diff --git a/meteor/client/lib/util.js b/meteor/client/lib/util.js index 2d46b711ee..29fcbf8b8c 100755 --- a/meteor/client/lib/util.js +++ b/meteor/client/lib/util.js @@ -1,6 +1,6 @@ var path = require('path'); var fs = require('fs'); -var https = require('https'); +var wget = require('wget'); var nodeCrypto = require('crypto'); Util = {}; @@ -95,25 +95,24 @@ Util.openTerminal = function (command) { }); }; -Util.downloadFile = function (url, filename, checksum, progressCallback, callback) { +Util.downloadFile = function (url, filename, checksum, callback, progressCallback) { var doDownload = function () { - var file = fs.createWriteStream(filename); - https.get(url, function(res) { - var len = 0; - res.on('data', function(chunk) { - file.write(chunk); - len += chunk.length; - - // percentage downloaded is as follows - var percent = (len / res.headers['content-length']) * 100; - progressCallback(percent); - }); - res.on('end', function() { - file.close(); - }); - file.on('close', function() { - callback(); - }); + var percent = 0; + var interval = setInterval(function () { + progressCallback(percent); + }, 250); + var download = wget.download(url, filename); + download.on('error', function (err) { + console.log(err); + clearInterval(interval); + }); + download.on('end', function (output) { + console.log(output); + callback(); + clearInterval(interval); + }); + download.on('progress', function (progress) { + percent = Math.round(progress * 100.0); }); }; @@ -122,10 +121,13 @@ Util.downloadFile = function (url, filename, checksum, progressCallback, callbac var existingChecksum = nodeCrypto.createHash('sha256').update(fs.readFileSync(filename), 'utf8').digest('hex'); console.log(existingChecksum); if (existingChecksum !== checksum) { + fs.unlinkSync(filename); doDownload(); } else { callback(); } + } else { + doDownload(); } }; diff --git a/meteor/client/lib/virtualbox.js b/meteor/client/lib/virtualbox.js index 8eba0b2f4a..ee172fb1bb 100644 --- a/meteor/client/lib/virtualbox.js +++ b/meteor/client/lib/virtualbox.js @@ -8,7 +8,7 @@ VirtualBox = {}; VirtualBox.REQUIRED_VERSION = '4.3.14'; VirtualBox.INCLUDED_VERSION = '4.3.14'; VirtualBox.INSTALLER_FILENAME = 'virtualbox-4.3.14.pkg'; -VirtualBox.INSTALLER_CHECKSUM = '486348a5336539728ca20dcd9674cf3d37e5c7f32255d90f1edc7391b54bd5dd'; +VirtualBox.INSTALLER_CHECKSUM = '486348a5336539728ca20dcd9674cf3d37e5c7f32255d90f1edc7391b54bd5dd'; // Sha256 Checksum // Info for the hostonly interface we add to the VM. VirtualBox.HOSTONLY_HOSTIP = '192.168.60.3'; @@ -26,7 +26,7 @@ VirtualBox.exec = function (command, callback) { VirtualBox.install = function (callback) { // -W waits for the process to close before finishing. - exec('open -W ' + path.join(Util.getResourceDir(), this.INSTALLER_FILENAME), function (error, stdout, stderr) { + exec('open -W ' + path.join(Util.getResourceDir(), this.INSTALLER_FILENAME).replace(' ', '\\ '), function (error, stdout, stderr) { console.log(stdout); console.log(stderr); if (error) { diff --git a/meteor/client/main.less b/meteor/client/main.less index b0d775924d..80701a0b9f 100755 --- a/meteor/client/main.less +++ b/meteor/client/main.less @@ -9,3 +9,4 @@ @import "stylesheets/dashboard.import.less"; @import "stylesheets/setup.import.less"; @import "stylesheets/spinner.import.less"; +@import "stylesheets/radial-progress.import.less"; diff --git a/meteor/client/stylesheets/radial-progress.import.less b/meteor/client/stylesheets/radial-progress.import.less new file mode 100644 index 0000000000..f522a827d0 --- /dev/null +++ b/meteor/client/stylesheets/radial-progress.import.less @@ -0,0 +1,91 @@ +.radial-progress { + @circle-size: 34px; + @circle-background: #d6dadc; + @circle-color: #3FD899; + @inset-size: 28px; + @inset-color: #fbfbfb; + @transition-length: 1s; + @shadow: 0px 1px 3px rgba(0,0,0,0.2); + @percentage-color: #3FD899; + @percentage-font-size: 11px; + @percentage-text-width: 57px; + + width: @circle-size; + height: @circle-size; + + background-color: @circle-background; + border-radius: 50%; + .circle { + .mask, .fill, .shadow { + width: @circle-size; + height: @circle-size; + position: absolute; + border-radius: 50%; + } + .shadow { + box-shadow: @shadow inset; + } + .mask, .fill { + -webkit-backface-visibility: hidden; + transition: -webkit-transform @transition-length; + transition: -ms-transform @transition-length; + transition: transform @transition-length; + border-radius: 50%; + } + .mask { + clip: rect(0px, @circle-size, @circle-size, @circle-size/2); + .fill { + clip: rect(0px, @circle-size/2, @circle-size, 0px); + background-color: @circle-color; + } + } + } + .inset { + width: @inset-size; + height: @inset-size; + position: absolute; + margin-left: (@circle-size - @inset-size)/2; + margin-top: (@circle-size - @inset-size)/2; + + background-color: @inset-color; + border-radius: 50%; + box-shadow: @shadow; + .percentage { + width: @percentage-text-width; + position: absolute; + top: (@inset-size - @percentage-font-size) / 2; + left: (@inset-size - @percentage-text-width) / 2; + + line-height: 1; + text-align: center; + + color: @percentage-color; + font-weight: 800; + font-size: @percentage-font-size; + } + } + + @i: 0; + @increment: 180deg / 100; + .loop (@i) when (@i <= 100) { + &[data-progress="@{i}"] { + .circle { + .mask.full, .fill { + -webkit-transform: rotate(@increment * @i); + -ms-transform: rotate(@increment * @i); + transform: rotate(@increment * @i); + } + .fill.fix { + -webkit-transform: rotate(@increment * @i * 2); + -ms-transform: rotate(@increment * @i * 2); + transform: rotate(@increment * @i * 2); + } + } + .inset .percentage:before { + content: "@{i}%" + } + } + .loop(@i + 1); + } + .loop(@i); +} \ No newline at end of file diff --git a/meteor/client/views/dashboard/setup/setup-install.html b/meteor/client/views/dashboard/setup/setup-install.html index a58b7c88b9..bb76f77e74 100644 --- a/meteor/client/views/dashboard/setup/setup-install.html +++ b/meteor/client/views/dashboard/setup/setup-install.html @@ -24,7 +24,11 @@ {{#if $eq this.index failedStep}} {{else}} - {{> spinner}} + {{#if currentInstallStepProgress}} + {{> radial_progress progress=currentInstallStepProgress}} + {{else}} + {{> spinner}} + {{/if}} {{/if}}
diff --git a/meteor/client/views/dashboard/setup/setup-install.js b/meteor/client/views/dashboard/setup/setup-install.js index d5774fbbd7..d080e2e12f 100644 --- a/meteor/client/views/dashboard/setup/setup-install.js +++ b/meteor/client/views/dashboard/setup/setup-install.js @@ -25,6 +25,9 @@ Template.setup_install.helpers({ currentInstallStep: function () { return Session.get('currentInstallStep'); }, + currentInstallStepProgress: function () { + return Session.get('currentInstallStepProgress'); + }, installComplete: function () { return Session.get('currentInstallStep') === Installer.steps.length; }, diff --git a/meteor/client/views/includes/radial-progress.html b/meteor/client/views/includes/radial-progress.html new file mode 100644 index 0000000000..56295c71b0 --- /dev/null +++ b/meteor/client/views/includes/radial-progress.html @@ -0,0 +1,17 @@ + \ No newline at end of file diff --git a/package.json b/package.json index 1d69d2db76..59b8d52df1 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "open": "0.0.5", "dockerode": "2.0.3", "tar": "0.1.20", - "ansi-to-html": "0.2.0" + "ansi-to-html": "0.2.0", + "wget": "0.0.1" } }