From 208b2d3f12644b2a340bfc36f2cc1ab2e360d482 Mon Sep 17 00:00:00 2001 From: Jeff Morgan Date: Sat, 30 Aug 2014 16:51:51 -0700 Subject: [PATCH 01/13] Hotfix for hanging on the last step of the installer. --- meteor/client/lib/boot2docker.js | 1 + 1 file changed, 1 insertion(+) diff --git a/meteor/client/lib/boot2docker.js b/meteor/client/lib/boot2docker.js index 8e939a2dc3..281d79c0c0 100644 --- a/meteor/client/lib/boot2docker.js +++ b/meteor/client/lib/boot2docker.js @@ -178,6 +178,7 @@ installBoot2DockerAddons = function (callback) { console.log(stdout); callback(err); }); + exec('VBoxManage modifyvm boot2docker-vm --nic2 hostonly --nictype2 virtio --cableconnected2 on --hostonlyadapter2 vboxnet0', function (err, stdout) {}); boot2dockerexec('ssh "sudo ifconfig eth1 192.168.59.103 netmask 255.255.255.0"', function (err, stdout) {}); exec('VBoxManage dhcpserver remove --netname HostInterfaceNetworking-vboxnet0', function (err, stdout) {}); }; From 0bedb1902ef7f298ef8b95f6225fe9d8048c4f8d Mon Sep 17 00:00:00 2001 From: Jeff Morgan Date: Mon, 1 Sep 2014 03:25:31 -0700 Subject: [PATCH 02/13] Fixing common installer bugs --- meteor/client/lib/boot2docker.js | 289 +++++++++--------- meteor/client/lib/installer.js | 146 +++++++++ meteor/client/lib/utilities.js | 75 +++++ meteor/client/lib/virtualbox.js | 181 ++++++++--- meteor/client/main.js | 33 +- .../dashboard/settings/dashboard-settings.js | 2 +- .../views/dashboard/setup/setup-install.js | 134 +------- meteor/server/docker.js | 4 +- resources/install | 39 +-- 9 files changed, 562 insertions(+), 341 deletions(-) create mode 100644 meteor/client/lib/installer.js diff --git a/meteor/client/lib/boot2docker.js b/meteor/client/lib/boot2docker.js index 281d79c0c0..879fb42a76 100644 --- a/meteor/client/lib/boot2docker.js +++ b/meteor/client/lib/boot2docker.js @@ -1,14 +1,54 @@ var exec = require('exec'); var path = require('path'); -boot2dockerexec = function (command, callback) { - exec(path.join(getBinDir(), 'boot2docker') + ' ' + command, function(err, stdout) { - callback(err, stdout); +Boot2Docker = {}; + +Boot2Docker.REQUIRED_IP = '192.168.60.103'; + +Boot2Docker.exec = function (command, callback) { + exec(path.join(getBinDir(), 'boot2docker') + ' ' + command, function(err, stdout, stderr) { + callback(err, stdout, stderr); }); }; -getBoot2DockerIp = function (callback) { - boot2dockerexec('ip', function (err, stdout) { +Boot2Docker.exists = function (callback) { + this.exec('info', function (err) { + if (err) { + callback(null, false); + } else { + callback(null, true); + } + }); +}; + +Boot2Docker.stop = function (callback) { + this.exec('stop', function (err, stdout) { + if (err) { + callback(err); + } else { + callback(null); + } + }); +}; + +Boot2Docker.erase = function (callback) { + var VMFileLocation = path.join(getHomePath(), 'VirtualBox\\ VMs/boot2docker-vm'); + exec('rm -rf ' + VMFileLocation, function (err) { + callback(err); + }); +}; + +Boot2Docker.upgrade = function (callback) { + var self = this; + self.stop(function (err) { + self.exec('upgrade', function (err, stdout) { + callback(err); + }); + }); +}; + +Boot2Docker.ip = function (callback) { + this.exec('ip', function (err, stdout) { if (err) { callback(err, null); } else { @@ -17,8 +57,51 @@ getBoot2DockerIp = function (callback) { }); }; -getBoot2DockerState = function (callback) { - boot2dockerexec(' info', function (err, stdout) { +Boot2Docker.setIp = function (ifname, ip, callback) { + this.exec('ssh "sudo ifconfig ' + ifname + ' ' + ip + ' netmask 255.255.255.0"', function (err, stdout) { + callback(err); + }); +}; + +Boot2Docker.init = function (callback) { + this.exec('init', function (err) { + callback(err); + }); +}; + +Boot2Docker.start = function (callback) { + var self = this; + self.exists(function (err, exists) { + if (!exists) { + callback('Cannot start if the boot2docker VM doesn\'t exist'); + return; + } + self.exec('up -v', function (err, stdout) { + console.log('here0'); + console.log('here1'); + // Sometimes boot2docker returns an error code even though it's working / waiting, so treat that as + // Success as well + if (!err || (err.indexOf('Waiting for VM to be started') !== -1 || err.indexOf('..........') !== -1)) { + Boot2Docker.setIp('eth2', Boot2Docker.REQUIRED_IP, function(err) { + console.log('here1'); + if (err) { callback(err); return; } + VirtualBox.removeDHCP(function (err) { + console.log('here2'); + self.injectUtilities(function (err) { + console.log('here3'); + callback(err); + }); + }); + }); + } else { + callback(err); + } + }); + }); +}; + +Boot2Docker.state = function (callback) { + this.exec('info', function (err, stdout) { if (err) { callback(err, null); return; @@ -32,8 +115,8 @@ getBoot2DockerState = function (callback) { }); }; -getBoot2DockerDiskUsage = function (callback) { - boot2dockerexec('ssh "df"', function (err, stdout) { +Boot2Docker.diskUsage = function (callback) { + this.exec('ssh "df"', function (err, stdout) { if (err) { callback(err, null); return; @@ -61,8 +144,8 @@ getBoot2DockerDiskUsage = function (callback) { }); }; -getBoot2DockerMemoryUsage = function (callback) { - boot2dockerexec('ssh "free -m"', function (err, stdout) { +Boot2Docker.memoryUsage = function (callback) { + this.exec('ssh "free -m"', function (err, stdout) { if (err) { callback(err, null); return; @@ -92,152 +175,80 @@ getBoot2DockerMemoryUsage = function (callback) { }); }; -getBoot2DockerInfo = function (callback) { - boot2dockerexec('ssh "sudo ifconfig eth1 192.168.59.103 netmask 255.255.255.0"', function (err, stdout) { - exec('VBoxManage dhcpserver remove --netname HostInterfaceNetworking-vboxnet0', function (err, stdout) { - getBoot2DockerState(function (err, state) { +Boot2Docker.stats = function (callback) { + this.state(function (err, state) { + if (err) { + callback(err, null); + return; + } + if (state === 'poweroff') { + callback(null, {state: state}); + return; + } + this.memoryUsage(function (err, mem) { if (err) { - callback(err, null); + callback(null, {state: state}); return; } - if (state === 'poweroff') { - callback(null, {state: state}); - } else { - getBoot2DockerMemoryUsage(function (err, mem) { - if (err) { callback(null, {state: state}); } - getBoot2DockerDiskUsage(function (err, disk) { - if (err) { callback(null, {state: state}); } - callback(null, { - state: state, - memory: mem, - disk: disk - }); - }); - }); - } - }); - }); - }); -}; - -boot2DockerVMExists = function (callback) { - boot2dockerexec('info', function (err) { - if (err) { - callback(null, false); - } else { - callback(null, true); - } - }); -}; - -eraseBoot2DockerVMFiles = function (callback) { - var VMFileLocation = path.join(getHomePath(), 'VirtualBox\\ VMs/boot2docker-vm'); - exec('rm -rf ' + VMFileLocation, function (err) { - callback(err); - }); -}; - -initBoot2Docker = function (callback) { - isVirtualBoxInstalled(function (err, installed) { - if (err) { - callback(err); - return; - } - if (installed) { - boot2dockerexec('init', function (err) { - console.log(err); + this.diskUsage(function (err, disk) { if (err) { - if (err.indexOf('exit status 1') !== -1) { - eraseBoot2DockerVMFiles(function () { - boot2dockerexec('init', function (err) { - callback(err); - }); - }); - } else { - callback(err); - } - } else { - callback(); + callback(null, {state: state}); + return; } + callback(null, { + state: state, + memory: mem, + disk: disk + }); }); + }); + }); +}; + +/** + * Get the VM's version. + * Node that this only works if the VM is up and running. + */ +Boot2Docker.vmVersion = function (callback) { + this.exec('ssh "cat /etc/version', function (err, stdout, stderr) { + if (err) { + callback(err); + return; } else { - callback(new Error('initBoot2Docker called but VirtualBox isn\'t installed.')); + callback(null, stdout); } }); }; -upgradeBoot2Docker = function (callback) { - boot2dockerexec('upgrade', function (err, stdout) { - console.log(stdout); - callback(err); +Boot2Docker.version = function (callback) { + this.exec('version', function (err, stdout, stderr) { + if (err) { + callback(err); + return; + } + var match = stdout.match(/Client version: v(\d\.\d\.\d)/); + if (!match || match.length < 2) { + callback('Could not parse the boot2docker cli version.') + } else { + callback(null, match[1]); + } }); }; -installBoot2DockerAddons = function (callback) { +Boot2Docker.injectUtilities = function (callback) { exec('/bin/cat ' + path.join(getBinDir(), 'kite-binaries.tar.gz') + ' | ' + path.join(getBinDir(), 'boot2docker') + ' ssh "tar zx -C /usr/local/bin"', function (err, stdout) { - console.log(stdout); callback(err); }); - exec('VBoxManage modifyvm boot2docker-vm --nic2 hostonly --nictype2 virtio --cableconnected2 on --hostonlyadapter2 vboxnet0', function (err, stdout) {}); - boot2dockerexec('ssh "sudo ifconfig eth1 192.168.59.103 netmask 255.255.255.0"', function (err, stdout) {}); - exec('VBoxManage dhcpserver remove --netname HostInterfaceNetworking-vboxnet0', function (err, stdout) {}); }; -startBoot2Docker = function (callback) { - isVirtualBoxInstalled(function (err, installed) { - if (err) { - callback(err); - return; - } - if (installed) { - boot2DockerVMExists(function (err, exists) { - if (exists) { - boot2dockerexec('up -v', function (err, stdout) { - console.log(err); - console.log(stdout); - if (err) { - if (err.indexOf('Waiting for VM to be started') !== -1 || err.indexOf('..........') !== -1) { - installBoot2DockerAddons(function (err) { - callback(err); - }); - } else { - callback(err); - } - } else { - installBoot2DockerAddons(function (err) { - callback(err); - }); - } - }); - } else { - callback(new Error('startBoot2Docker called but boot2docker-vm doesn\'t exist.')); - } - }); - } else { - callback(new Error('startBoot2Docker called but VirtualBox isn\'t installed.')); - } - }); -}; - -stopBoot2Docker = function (callback) { - boot2dockerexec('stop', function (err, stdout) { - console.log(stdout); - console.log(err); - if (err) { - callback(err); - return; - } - callback(null); - }); -}; - -checkBoot2DockerVM = function (callback) { - boot2DockerVMExists(function (err) { +Boot2Docker.check = function (callback) { + var self = this; + self.exists(function (err) { if (err) { callback(err); return; } else { - getBoot2DockerState(function (err, state) { + self.state(function (err, state) { if (state !== 'running') { callback('boot2docker not running'); } else { @@ -248,10 +259,9 @@ checkBoot2DockerVM = function (callback) { }); }; -// Make sure the VM exists, is up and is running. -resolveBoot2DockerVM = function (callback) { - boot2DockerVMExists(function (err, exists) { - +Boot2Docker.resolve = function (callback) { + var self = this; + self.exists(function (err, exists) { // If somehow the boot2docker VM doesn't exist anymor then re-create it. if (!exists) { initBoot2Docker(function () { @@ -260,9 +270,8 @@ resolveBoot2DockerVM = function (callback) { }); }); } else { - // If it exists but it's not running.. restart it. - getBoot2DockerState(function (err, state) { + self.state(function (err, state) { if (state !== 'running') { startBoot2Docker(function (err) { callback(err); diff --git a/meteor/client/lib/installer.js b/meteor/client/lib/installer.js new file mode 100644 index 0000000000..39e043c2b8 --- /dev/null +++ b/meteor/client/lib/installer.js @@ -0,0 +1,146 @@ +var async = require('async'); + +Installer = {}; + +/** + * Install steps. A step is a function that accepts a function (err) callback and returns once that step is complete.keys: + * - run: Function that runs the installation step and calls the callback with an error if failed. + * - pastMessage: Message to show after step completion + * - message: Message to show while step is running + * - imperativeMessage: Message to show before running + */ +Installer.steps = [ + { + run: function (callback) { + var installed = VirtualBox.installed(); + if (!installed) { + VirtualBox.install(function (err) { + callback(err); + }); + } else { + // Version 4.3.12 is required. + VirtualBox.version(function (err, installedVersion) { + if (err) { + callback(err); + return; + } + var needsUpdate = Utilities.compareVersions(installedVersion, VirtualBox.REQUIRED_VERSION) < 0; + if (needsUpdate) { + VirtualBox.install(function (err) { + callback(err); + }); + } else { + callback(); + } + }); + } + }, + pastMessage: 'VirtualBox installed', + message: 'Installing VirtualBox', + futureMessage: 'Install VirtualBox if necessary' + }, + + // Initialize Boot2Docker if necessary. + { + run: function (callback) { + Boot2Docker.exists(function (err, exists) { + if (err) { + callback(err); + return; + } + if (!exists) { + Boot2Docker.init(function (err) { + callback(err); + }); + } else { + Boot2Docker.stop(function (err) { + Boot2Docker.upgrade(function (err) { + callback(err); + }); + }); + } + }); + }, + pastMessage: 'Setup the Boot2Docker VM (if required)', + message: 'Setting up the Boot2Docker VM', + futureMessage: 'Set up the Boot2Docker VM(if required)' + }, + + // Set up the routing. + { + run: function (callback) { + VirtualBox.setupRouting('boot2docker-vm', function (err, ifname) { + callback(err); + }); + }, + pastMessage: 'Container routing set up.', + message: 'Setting up container routing (root required).', + subMessage: '(This may take a few minutes)', + futureMessage: 'Set up container routing to VM (root required).' + }, + + // Start the Kitematic VM + { + run: function (callback) { + Boot2Docker.state(function (err, state) { + if (state !== 'running') { + Boot2Docker.start(function (err) { + callback(err); + }); + } else { + Boot2Docker.setIp('eth2', Boot2Docker.REQUIRED_IP, function(err) { + callback(err); + }); + } + }); + }, + pastMessage: 'Started the Boot2Docker VM', + message: 'Starting the Boot2Docker VM', + subMessage: '(This may take a few minutes)', + futureMessage: 'Start the Kitematic VM', + }, + + // Set up the default Kitematic images + { + run: function (callback) { + Meteor.call('reloadDefaultContainers', function (err) { + callback(err); + }); + }, + pastMessage: 'Started the Boot2Docker VM', + message: 'Setting up the default Kitematic images...', + subMessage: '(This may take a few minutes)', + futureMessage: 'Set up the default Kitematic images', + } +]; + +Installer.run = function (callback) { + var currentStep = 0; + Session.set('currentInstallStep', currentStep); + Session.set('numberOfInstallSteps', this.steps.length); + async.eachSeries(this.steps, function (step, callback) { + console.log('Performing step ' + currentStep); + step.run(function (err) { + if (err) { + callback(err); + } else { + currentStep += 1; + Session.set('currentInstallStep', currentStep); + callback(); + } + }); + }, function (err) { + if (err) { + // if any of the steps fail + console.log('Kitematic setup failed at step ' + currentStep); + console.log(err); + Session.set('failedStep', currentStep); + Session.set('failedError', err); + callback(err); + } else { + // Setup Finished + console.log('Setup finished.'); + callback(); + } + }); +}; diff --git a/meteor/client/lib/utilities.js b/meteor/client/lib/utilities.js index c3fe268798..8915f19d46 100755 --- a/meteor/client/lib/utilities.js +++ b/meteor/client/lib/utilities.js @@ -1,5 +1,80 @@ var path = require('path'); +Utilities = {}; + +/** + * Compares two software version numbers (e.g. "1.7.1" or "1.2b"). + * + * @param {string} v1 The first version to be compared. + * @param {string} v2 The second version to be compared. + * @param {object} [options] Optional flags that affect comparison behavior: + * + * @returns {number|NaN} + * + * + */ +Utilities.compareVersions = function (v1, v2, options) { + var lexicographical = options && options.lexicographical, + zeroExtend = options && options.zeroExtend, + v1parts = v1.split('.'), + v2parts = v2.split('.'); + + function isValidPart(x) { + return (lexicographical ? /^\d+[A-Za-z]*$/ : /^\d+$/).test(x); + } + + if (!v1parts.every(isValidPart) || !v2parts.every(isValidPart)) { + return NaN; + } + + if (zeroExtend) { + while (v1parts.length < v2parts.length) v1parts.push('0'); + while (v2parts.length < v1parts.length) v2parts.push('0'); + } + + if (!lexicographical) { + v1parts = v1parts.map(Number); + v2parts = v2parts.map(Number); + } + + for (var i = 0; i < v1parts.length; ++i) { + if (v2parts.length == i) { + return 1; + } + + if (v1parts[i] == v2parts[i]) { + continue; + } + else if (v1parts[i] > v2parts[i]) { + return 1; + } + else { + return -1; + } + } + + if (v1parts.length != v2parts.length) { + return -1; + } + + return 0; +}; + getBinDir = function () { if (process.env.NODE_ENV === 'development') { return path.join(path.join(process.env.PWD, '..'), 'resources'); diff --git a/meteor/client/lib/virtualbox.js b/meteor/client/lib/virtualbox.js index 6d362492a9..cbf3290293 100644 --- a/meteor/client/lib/virtualbox.js +++ b/meteor/client/lib/virtualbox.js @@ -1,55 +1,168 @@ var fs = require('fs'); -var child_process = require('child_process'); +var exec = require('exec'); var path = require('path'); -isVirtualBoxInstalled = function (callback) { - fs.exists('/usr/bin/VBoxManage', function (exists) { - callback(null, exists); +VirtualBox = {}; + +VirtualBox.REQUIRED_VERSION = '4.3.12'; +VirtualBox.INCLUDED_VERSION = '4.3.12'; +VirtualBox.INSTALLER_FILENAME = 'virtualbox-4.3.12.pkg'; + +// Info for the hostonly interface we add to the VM. +VirtualBox.HOSTONLY_HOSTIP = '192.168.60.3'; +VirtualBox.HOSTONLY_NETWORKMASK = '255.255.255.0'; + +VirtualBox.installed = function () { + return fs.existsSync('/usr/bin/VBoxManage'); +}; + +VirtualBox.exec = function (command, callback) { + exec('/usr/bin/VBoxManage ' + command, function (error, stdout, stderr) { + callback(error, stdout, stderr); }); }; -isResolverSetup = function (callback) { - fs.readFile('/etc/resolver/dev', { - encoding: 'utf8' - }, function (err, data) { +VirtualBox.install = function (callback) { + // -W waits for the process to close before finishing. + exec('open -W ' + path.join(getBinDir(), this.INSTALLER_FILENAME), function (error, stdout, stderr) { + if (error) { + callback(error); + return; + } + callback(null); + }); +} + +VirtualBox.version = function (callback) { + if (!this.installed()) { + callback('VirtualBox not installed.'); + return; + } + this.exec('-v', function (err, stdout, stderr) { if (err) { - callback(err, false); - } else { - if (data.indexOf('nameserver 172.17.42.1') !== -1) { - callback(null, true); - } else { - callback(null, false); + callback(err); + return; + } + // Output is x.x.xryyyyyy + var match = stdout.match(/(\d+\.\d+\.\d+).*/); + if (!match || match.length < 2) { + callback('VBoxManage -v output format not recognized.'); + return; + } + callback(null, match[1]); + }); +}; + +VirtualBox.hostOnlyIfs = function (callback) { + this.exec('list hostonlyifs', function (err, stdout, stderr) { + if (err) { + callback(err); + return; + } + var lines = stdout.split('\n'); + var hostOnlyIfs = {}; + var currentIf = null; + _.each(lines, function (line) { + if (!line.length) return; + var pieces = line.split(':'); + var key = pieces[0].trim(); + var value = pieces[1] ? pieces[1].trim() : null; + if (key === 'Name') { + currentIf = value; + hostOnlyIfs[value] = {}; } - } + hostOnlyIfs[currentIf][key] = value; + }); + callback(null, hostOnlyIfs); }); }; -setupResolver = function (callback) { - var installFile = path.join(getBinDir(), 'install'); - var cocoaSudo = path.join(getBinDir(), 'cocoasudo'); - var execCommand = cocoaSudo + ' --prompt="Kitematic Setup wants to make changes. Type your password to allow this." ' + installFile; - child_process.exec(execCommand, function (error, stdout, stderr) { - console.log(stdout); - if (error) { - console.log(error); - callback(error); +VirtualBox.hostOnlyAdapters = function (vm, callback) { + this.exec('showvminfo ' + vm + ' --machinereadable', function (err, stdout, stderr) { + if (err) { + callback(err); return; } - console.log('Virtualbox Installation & Resolver config complete.'); - callback(); + var matches = stdout.match(/(hostonlyadapter\d+)="(vboxnet\d+)"/g); + if (!matches.length) { + callback(null, {}); + } else { + var objs = {}; + _.each(matches, function (match) { + var pieces = match.split('='); + objs[pieces[0]] = pieces[1].replace(/"/g, ''); + }); + callback(null, objs); + } }); }; -setupVirtualBox = function (callback) { - child_process.exec('open -W ' + path.join(getBinDir(), 'virtualbox-4.3.12.pkg'), function (error, stdout, stderr) { - console.log(stdout); - if (error) { - console.log(error); - callback(error); +VirtualBox.hostOnlyAdapter = function (callback) { + var self = this; + self.hostOnlyIfs(function (err, ifs) { + var iface = _.findWhere(_.toArray(ifs), {IPAddress: VirtualBox.HOSTONLY_HOSTIP}); + if (!iface) { + self.exec('hostonlyif create', function (err, stdout, stderr) { + var match = stdout.match(/Interface '(vboxnet\d+)' was successfully created/); + console.log(match); + if (!match) { + callback('Could not parse output of hostonlyif create'); + return; + } + self.exec('hostonlyif ipconfig ' + match[1] + ' --ip ' + VirtualBox.HOSTONLY_HOSTIP + ' --netmask ' + VirtualBox.HOSTONLY_NETWORKMASK, function(err, stdout, stderr) { + if (err) { + callback(err); + return; + } + callback(null, match[1]); + }); + }); + } else { + callback(null, iface.Name); + } + }); +}; + +VirtualBox.addCustomHostAdapter = function (vm, callback) { + var self = this; + self.hostOnlyAdapter(function (err, ifname) { + if (err) { + callback(err); return; } - console.log('Virtualbox Installation running.'); - callback(); + self.exec('modifyvm ' + vm + ' --nic3 hostonly --nictype3 virtio --cableconnected3 on --hostonlyadapter3 ' + ifname, function (err, stdout, stderr) { + callback(err, ifname); + }); }); }; +VirtualBox.setupRouting = function (vm, callback) { + // Get the host only adapter or create it if it doesn't exist + this.addCustomHostAdapter(vm, function (err, ifname) { + var installFile = path.join(getBinDir(), 'install'); + var cocoaSudo = path.join(getBinDir(), 'cocoasudo'); + var execCommand = cocoaSudo + ' --prompt="Kitematic needs your password to allow routing *.dev requests to containers." ' + installFile; + exec(execCommand, {env: {IFNAME: ifname, GATEWAY: Boot2Docker.REQUIRED_IP}}, function (error, stdout, stderr) { + if (error) { + callback(error); + return; + } + callback(); + }); + }); +}; + + +VirtualBox.removeDHCP = function (callback) { + var self = this; + self.hostOnlyAdapter(function (err, ifname) { + if (err) { callback(err); return; } + console.log(ifname); + self.exec('dhcpserver remove --ifname ' + ifname, function (err, stdout, stderr) { + callback(err); + }); + }); +}; + + + diff --git a/meteor/client/main.js b/meteor/client/main.js index 7abe7d59a1..b021d84992 100755 --- a/meteor/client/main.js +++ b/meteor/client/main.js @@ -68,19 +68,6 @@ Meteor.call('getDockerHost', function (err, host) { Session.set('dockerHost', host); }); -updateBoot2DockerInfo = function () { - getBoot2DockerInfo(function (err, info) { - if (err) { - return; - } - Session.set('boot2dockerState', info.state); - if (info.state !== 'poweroff' && info.memory && info.disk) { - Session.set('boot2dockerMemoryUsage', info.memory); - Session.set('boot2dockerDiskUsage', info.disk); - } - }); -}; - fixBoot2DockerVM = function (callback) { checkBoot2DockerVM(function (err) { if (err) { @@ -138,7 +125,24 @@ fixDefaultContainers = function (callback) { }; Meteor.setInterval(function () { - updateBoot2DockerInfo(); + Boot2Docker.exists(function (err, exists) { + if (err) { return; } + if (exists) { + Boot2Docker.state(function (err, state) { + if (err) { return; } + if (state === 'running') { + Boot2Docker.info(function (err, info) { + if (err) { return; } + Session.set('boot2dockerState', info.state); + if (info.state !== 'poweroff' && info.memory && info.disk) { + Session.set('boot2dockerMemoryUsage', info.memory); + Session.set('boot2dockerDiskUsage', info.disk); + } + }); + } + }); + } + }); }, 5000); fixInterval = null; @@ -148,7 +152,6 @@ startFixInterval = function () { resolveWatchers(function () {}); fixBoot2DockerVM(function (err) { if (err) { console.log(err); return; } - // Meteor.call('recoverApps'); fixDefaultImages(function (err) { if (err) { console.log(err); return; } fixDefaultContainers(function (err) { diff --git a/meteor/client/views/dashboard/settings/dashboard-settings.js b/meteor/client/views/dashboard/settings/dashboard-settings.js index cf12f91c27..5ee50c0778 100644 --- a/meteor/client/views/dashboard/settings/dashboard-settings.js +++ b/meteor/client/views/dashboard/settings/dashboard-settings.js @@ -3,7 +3,7 @@ Template.dashboard_settings.events({ var $btn = $(e.currentTarget); $btn.html('Starting Boot2Docker...'); $btn.attr("disabled", "disabled"); - startFixInterval(); + //startFixInterval(); startBoot2Docker(function (err) { if (err) { console.error(err); } }); diff --git a/meteor/client/views/dashboard/setup/setup-install.js b/meteor/client/views/dashboard/setup/setup-install.js index 004ac755cf..020e86413b 100644 --- a/meteor/client/views/dashboard/setup/setup-install.js +++ b/meteor/client/views/dashboard/setup/setup-install.js @@ -1,136 +1,8 @@ -var async = require('async'); - -// Install steps. A step is a function that accepts a function (err) callback and returns once that step is complete. -// keys: -// - install: Function that runs the installation step and calls the callback with an error if failed. -// - pastMessage: Message to show after step completion -// - message: Message to show while step is running -// - imperativeMessage: Message to show before running -var steps = [ - - // Set up VirtualBox - { - install: function (callback) { - isVirtualBoxInstalled(function (err, virtualBoxInstalled) { - var installedYet = false; - if (!virtualBoxInstalled) { - setupVirtualBox(function (err) { - callback(err); - }); - } else { - callback(); - } - }); - }, - pastMessage: 'VirtualBox installed', - message: 'Installing VirtualBox', - futureMessage: 'Install VirtualBox if necessary' - }, - - // Set up the routing. - { - install: function (callback) { - setupResolver(function (err) { - callback(err); - }); - }, - pastMessage: 'Container routing set up (root required).', - message: 'Setting up container routing (root required).', - subMessage: '(This may take a few minutes)', - futureMessage: 'Set up container routing to VM (root required).' - }, - - // Set up the VM for running Kitematic apps - { - install: function (callback) { - console.log('Checking if vm exists...'); - boot2DockerVMExists(function (err, exists) { - console.log('VM exists: ' + exists); - if (exists) { - console.log('Stopping vm'); - stopBoot2Docker(function () { - console.log('Upgrading vm'); - upgradeBoot2Docker(function () { - callback(); - }); - }); - } else { - console.log('init VM'); - initBoot2Docker(function () { - callback(); - }); - } - }); - }, - pastMessage: 'Set up the Kitematic VM', - message: 'Setting up the Kitematic VM...', - futureMessage: 'Set up the Kitematic VM' - }, - - // Start the Kitematic VM - { - install: function (callback) { - startBoot2Docker(function (err) { - callback(err); - }); - }, - pastMessage: 'Started the Kitematic VM', - message: 'Starting the Kitematic VM', - subMessage: '(This may take a few minutes)', - futureMessage: 'Start the Kitematic VM', - }, - - // Set up the default Kitematic images - { - install: function (callback) { - Meteor.call('reloadDefaultContainers', function (err) { - callback(err); - }); - }, - pastMessage: 'Started the Kitematic VM', - message: 'Setting up the default Kitematic images...', - subMessage: '(This may take a few minutes)', - futureMessage: 'Set up the default Kitematic images', - } -]; - -runSetup = function (callback) { - // Run through the Kitematic installation, skipping steps if required. - var currentStep = 0; - Session.set('currentInstallStep', currentStep); - Session.set('numberOfInstallSteps', steps.length); - async.eachSeries(steps, function (step, callback) { - console.log('Performing step ' + currentStep); - step.install(function (err) { - if (err) { - callback(err); - } else { - currentStep += 1; - Session.set('currentInstallStep', currentStep); - callback(); - } - }); - }, function (err) { - if (err) { - // if any of the steps fail - console.log('Kitematic setup failed at step ' + currentStep); - console.log(err); - Session.set('failedStep', currentStep); - Session.set('failedError', err); - callback(err); - } else { - // Setup Finished - console.log('Setup finished.'); - callback(); - } - }); -}; - var installStarted = false; Template.setup_install.rendered = function() { if(!installStarted) { installStarted = true; - runSetup(function (err) { + Installer.run(function (err) { if (err) { console.log('Setup failed.'); console.log(err); @@ -144,7 +16,7 @@ Template.setup_install.rendered = function() { }; Template.setup_install.steps = function () { - return steps.map(function (step, index) { + return Installer.steps.map(function (step, index) { step.index = index; return step; }); @@ -155,7 +27,7 @@ Template.setup_install.helpers({ return Session.get('currentInstallStep'); }, installComplete: function () { - return Session.get('currentInstallStep') === steps.length; + return Session.get('currentInstallStep') === Installer.steps.length; }, failedStep: function () { return Session.get('failedStep'); diff --git a/meteor/server/docker.js b/meteor/server/docker.js index d71b12688b..6aded68a47 100755 --- a/meteor/server/docker.js +++ b/meteor/server/docker.js @@ -3,8 +3,8 @@ Docker = Meteor.require('dockerode'); var Convert = Meteor.require('ansi-to-html'); var convert = new Convert(); -var DOCKER_HOST='192.168.59.103'; -docker = new Docker({host: '192.168.59.103', port: '2375'}); +var DOCKER_HOST='192.168.60.103'; +docker = new Docker({host: DOCKER_HOST, port: '2375'}); hasDockerfile = function (directory) { return fs.existsSync(path.join(directory, 'Dockerfile')); diff --git a/resources/install b/resources/install index 723ff280d1..b0791ac3bc 100755 --- a/resources/install +++ b/resources/install @@ -1,6 +1,11 @@ #!/bin/sh -# Lookup the Kitematic VM resolver for .dev domains +# This script must be run as root and sets up Mac OS X to route all .dev domains to the virtual box VM with the name +# 'boot2docker-vm'. It does the following: +# 1) Adds a file under /etc/resolver/dev +# 2) Sets up a LaunchAgent for adding entries to the route table to route all requests to the Docker subnet (172.17.0.0/16) +# And expects the $IFNAME variable to contain the interface on which to send traffic to the boot2docker VM. + mkdir -p /etc/resolver echo "nameserver 172.17.42.1" > /etc/resolver/dev @@ -8,10 +13,9 @@ DIR=$(dirname "$0") USER=`w -h | sort -u -t' ' -k1,1 | awk '{print $1}'` /bin/rm -rf /Library/LaunchAgents/com.kitematic.route.plist - -echo ' - - +echo " + + Label com.kitematic.route @@ -20,26 +24,25 @@ echo ' /sbin/route -n add - 172.17.0.0/16 - 192.168.59.103 + -net + 172.17.0.0 + -netmask + 255.255.0.0 + -iface + $IFNAME + 255.255.0.0 + -gateway + $GATEWAY KeepAlive RunAtLoad - ServiceIPC - - UserName - root LaunchOnlyOnce -' > /Library/LaunchAgents/com.kitematic.route.plist - -sudo -u $USER $DIR/boot2docker init -VBoxManage modifyvm boot2docker-vm --nic2 hostonly --nictype2 virtio --cableconnected2 on --hostonlyadapter2 vboxnet0 -VBoxManage dhcpserver add --netname=vboxnet0 --ip=192.168.59.99 --netmask=255.255.255.0 --lowerip=192.168.59.103 --upperip=192.168.59.103 +" > /Library/LaunchAgents/com.kitematic.route.plist # Add entries to routing table for Kitematic VM -/sbin/route delete 172.17.0.0/16 192.168.59.103 -/sbin/route -n add 172.17.0.0/16 192.168.59.103 \ No newline at end of file +/sbin/route delete -net 172.17.0.0 -netmask 255.255.0.0 -gateway $GATEWAY +/sbin/route -n add -net 172.17.0.0 -netmask 255.255.0.0 -iface $IFNAME -gateway $GATEWAY \ No newline at end of file From 3ac3c9aa9eb43194c1fa13bb4e7d0448dae7be48 Mon Sep 17 00:00:00 2001 From: Liban Mohamed Date: Mon, 1 Sep 2014 14:17:35 -0400 Subject: [PATCH 03/13] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c0559fd0ba..c8cf734076 100755 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Keep track of development and community news. ## Versioning -For transparency into our release cycle and in striving to maintain backward compatibility, Kitematic is maintained under the [Semantic Versioning Guidelines](http://semver.org/). We'll try very hard adhere to those rules whenever possible. +For transparency into our release cycle and in striving to maintain backward compatibility, Kitematic is maintained under the [Semantic Versioning Guidelines](http://semver.org/). We'll try very hard to adhere to those rules whenever possible. ## Creators From c370a4cf898ffa064145e7821417511c81845c3b Mon Sep 17 00:00:00 2001 From: Jeff Morgan Date: Mon, 1 Sep 2014 15:42:15 -0700 Subject: [PATCH 04/13] Fixing remaining installer bugs --- meteor/client/lib/boot2docker.js | 24 ++++++-------- meteor/client/lib/installer.js | 32 ++++++++++++------- meteor/client/lib/virtualbox.js | 13 ++------ meteor/client/main.js | 32 ++++++++++--------- .../dashboard/settings/dashboard-settings.js | 16 ++++++---- resources/install | 6 ++-- 6 files changed, 63 insertions(+), 60 deletions(-) diff --git a/meteor/client/lib/boot2docker.js b/meteor/client/lib/boot2docker.js index 714f901902..b1cb48837f 100644 --- a/meteor/client/lib/boot2docker.js +++ b/meteor/client/lib/boot2docker.js @@ -81,6 +81,7 @@ Boot2Docker.start = function (callback) { // Success as well if (!err || (err.indexOf('Waiting for VM to be started') !== -1 || err.indexOf('..........') !== -1)) { self.correct(function (err) { + if (err) { callback(err); return; } self.injectUtilities(function (err) { callback(err); }); @@ -96,17 +97,14 @@ Boot2Docker.correct = function (callback) { Boot2Docker.setIp('eth2', Boot2Docker.REQUIRED_IP, function(err) { if (err) { callback(err); return; } VirtualBox.removeDHCP(function (err) { - callback(err); + callback(); }); }); }; Boot2Docker.state = function (callback) { - this.exec('info', function (err, stdout) { - if (err) { - callback(err, null); - return; - } + this.exec('info', function (err, stdout, stderr) { + if (err) { callback(err, null); return; } try { var info = JSON.parse(stdout); callback(null, info.State); @@ -177,23 +175,21 @@ Boot2Docker.memoryUsage = function (callback) { }; Boot2Docker.stats = function (callback) { - this.state(function (err, state) { - if (err) { - callback(err, null); - return; - } + var self = this; + self.state(function (err, state) { + if (err) { callback(err, null); return; } if (state === 'poweroff') { callback(null, {state: state}); return; } - this.memoryUsage(function (err, mem) { + self.memoryUsage(function (err, mem) { if (err) { callback(null, {state: state}); return; } - this.diskUsage(function (err, disk) { + self.diskUsage(function (err, disk) { if (err) { - callback(null, {state: state}); + callback(null, {state: state, memory: mem}); return; } callback(null, { diff --git a/meteor/client/lib/installer.js b/meteor/client/lib/installer.js index 39e043c2b8..76a3465e0f 100644 --- a/meteor/client/lib/installer.js +++ b/meteor/client/lib/installer.js @@ -24,7 +24,7 @@ Installer.steps = [ callback(err); return; } - var needsUpdate = Utilities.compareVersions(installedVersion, VirtualBox.REQUIRED_VERSION) < 0; + var needsUpdate = Util.compareVersions(installedVersion, VirtualBox.REQUIRED_VERSION) < 0; if (needsUpdate) { VirtualBox.install(function (err) { callback(err); @@ -44,16 +44,14 @@ Installer.steps = [ { run: function (callback) { Boot2Docker.exists(function (err, exists) { - if (err) { - callback(err); - return; - } + if (err) { callback(err); return; } if (!exists) { Boot2Docker.init(function (err) { callback(err); }); } else { Boot2Docker.stop(function (err) { + if (err) { callback(err); return; } Boot2Docker.upgrade(function (err) { callback(err); }); @@ -66,24 +64,24 @@ Installer.steps = [ futureMessage: 'Set up the Boot2Docker VM(if required)' }, - // Set up the routing. { run: function (callback) { - VirtualBox.setupRouting('boot2docker-vm', function (err, ifname) { + VirtualBox.addCustomHostAdapter('boot2docker-vm', function (err, ifname) { callback(err); }); }, - pastMessage: 'Container routing set up.', - message: 'Setting up container routing (root required).', - subMessage: '(This may take a few minutes)', - futureMessage: 'Set up container routing to VM (root required).' + 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' }, // Start the Kitematic VM { run: function (callback) { Boot2Docker.state(function (err, state) { + if (err) { callback(err); return; } if (state !== 'running') { + console.log('starting'); Boot2Docker.start(function (err) { callback(err); }); @@ -96,10 +94,20 @@ Installer.steps = [ }, pastMessage: 'Started the Boot2Docker VM', message: 'Starting the Boot2Docker VM', - subMessage: '(This may take a few minutes)', futureMessage: 'Start the Kitematic VM', }, + { + run: function (callback) { + VirtualBox.setupRouting('boot2docker-vm', function (err, ifname) { + callback(err); + }); + }, + pastMessage: 'Container routing set up', + message: 'Setting up container routing (root required)', + futureMessage: 'Set up container routing to VM (root required)' + }, + // Set up the default Kitematic images { run: function (callback) { diff --git a/meteor/client/lib/virtualbox.js b/meteor/client/lib/virtualbox.js index 73a23c27b8..4368f5c72f 100644 --- a/meteor/client/lib/virtualbox.js +++ b/meteor/client/lib/virtualbox.js @@ -106,16 +106,12 @@ VirtualBox.hostOnlyAdapter = function (callback) { if (!iface) { self.exec('hostonlyif create', function (err, stdout, stderr) { var match = stdout.match(/Interface '(vboxnet\d+)' was successfully created/); - console.log(match); if (!match) { callback('Could not parse output of hostonlyif create'); return; } self.exec('hostonlyif ipconfig ' + match[1] + ' --ip ' + VirtualBox.HOSTONLY_HOSTIP + ' --netmask ' + VirtualBox.HOSTONLY_NETWORKMASK, function(err, stdout, stderr) { - if (err) { - callback(err); - return; - } + if (err) { callback(err); return; } callback(null, match[1]); }); }); @@ -128,10 +124,7 @@ VirtualBox.hostOnlyAdapter = function (callback) { VirtualBox.addCustomHostAdapter = function (vm, callback) { var self = this; self.hostOnlyAdapter(function (err, ifname) { - if (err) { - callback(err); - return; - } + if (err) { callback(err); return; } self.exec('modifyvm ' + vm + ' --nic3 hostonly --nictype3 virtio --cableconnected3 on --hostonlyadapter3 ' + ifname, function (err, stdout, stderr) { callback(err, ifname); }); @@ -143,7 +136,7 @@ VirtualBox.setupRouting = function (vm, callback) { this.addCustomHostAdapter(vm, function (err, ifname) { var installFile = path.join(Util.getBinDir(), 'install'); var cocoaSudo = path.join(Util.getBinDir(), 'cocoasudo'); - var execCommand = cocoaSudo + ' --prompt="Kitematic needs your password to allow routing *.dev requests to containers." ' + installFile; + var execCommand = cocoaSudo + ' --prompt="Kitematic needs your password to allow routing *.kite requests to containers." ' + installFile; exec(execCommand, {env: {IFNAME: ifname, GATEWAY: Boot2Docker.REQUIRED_IP}}, function (error, stdout, stderr) { if (error) { callback(error); diff --git a/meteor/client/main.js b/meteor/client/main.js index 8d852723bf..0033c360b4 100755 --- a/meteor/client/main.js +++ b/meteor/client/main.js @@ -126,17 +126,17 @@ fixDefaultContainers = function (callback) { Meteor.setInterval(function () { Boot2Docker.exists(function (err, exists) { - if (err) { return; } + if (err) { console.log(err); return; } if (exists) { Boot2Docker.state(function (err, state) { - if (err) { return; } + if (err) { console.log(err); return; } + Session.set('boot2dockerState', state); if (state === 'running') { - Boot2Docker.info(function (err, info) { - if (err) { return; } - Session.set('boot2dockerState', info.state); - if (info.state !== 'poweroff' && info.memory && info.disk) { - Session.set('boot2dockerMemoryUsage', info.memory); - Session.set('boot2dockerDiskUsage', info.disk); + Boot2Docker.stats(function (err, stats) { + if (err) { console.log(err); return; } + if (stats.state !== 'poweroff' && stats.memory && stats.disk) { + Session.set('boot2dockerMemoryUsage', stats.memory); + Session.set('boot2dockerDiskUsage', stats.disk); } }); } @@ -148,16 +148,18 @@ Meteor.setInterval(function () { Meteor.setInterval(function () { if (Installs.findOne()) { resolveWatchers(function () {}); - fixBoot2DockerVM(function (err) { - if (err) { console.log(err); return; } - Meteor.call('recoverApps'); - fixDefaultImages(function (err) { + if (!Session.get('boot2dockerOff')) { + fixBoot2DockerVM(function (err) { if (err) { console.log(err); return; } - fixDefaultContainers(function (err) { - if (err) { console.log(err); } + Meteor.call('recoverApps'); + fixDefaultImages(function (err) { + if (err) { console.log(err); return; } + fixDefaultContainers(function (err) { + if (err) { console.log(err); } + }); }); }); - }); + } } }, 5000); diff --git a/meteor/client/views/dashboard/settings/dashboard-settings.js b/meteor/client/views/dashboard/settings/dashboard-settings.js index 5ee50c0778..1a2ef4b7bd 100644 --- a/meteor/client/views/dashboard/settings/dashboard-settings.js +++ b/meteor/client/views/dashboard/settings/dashboard-settings.js @@ -3,18 +3,22 @@ Template.dashboard_settings.events({ var $btn = $(e.currentTarget); $btn.html('Starting Boot2Docker...'); $btn.attr("disabled", "disabled"); - //startFixInterval(); - startBoot2Docker(function (err) { - if (err) { console.error(err); } + Session.set('boot2dockerOff', false); + Boot2Docker.start(function (err) { + if (err) { + console.log(err); + } }); }, 'click .btn-stop-boot2docker': function (e) { var $btn = $(e.currentTarget); $btn.html('Stopping Boot2Docker...'); $btn.attr("disabled", "disabled"); - stopFixInterval(); - stopBoot2Docker(function (err) { - if (err) { console.error(err); } + Session.set('boot2dockerOff', true); + Boot2Docker.stop(function (err) { + if (err) { + console.log(err); + } }); } }); diff --git a/resources/install b/resources/install index afde39e80b..f558efe007 100755 --- a/resources/install +++ b/resources/install @@ -1,13 +1,13 @@ #!/bin/sh -# This script must be run as root and sets up Mac OS X to route all .dev domains to the virtual box VM with the name +# This script must be run as root and sets up Mac OS X to route all .kite domains to the virtual box VM with the name # 'boot2docker-vm'. It does the following: -# 1) Adds a file under /etc/resolver/dev +# 1) Adds a file under /etc/resolver/kite # 2) Sets up a LaunchAgent for adding entries to the route table to route all requests to the Docker subnet (172.17.0.0/16) # And expects the $IFNAME variable to contain the interface on which to send traffic to the boot2docker VM. mkdir -p /etc/resolver -echo "nameserver 172.17.42.1" > /etc/resolver/dev +echo "nameserver 172.17.42.1" > /etc/resolver/kite DIR=$(dirname "$0") USER=`w -h | sort -u -t' ' -k1,1 | awk '{print $1}'` From 21f4ca056525311f59f2c108494b508eff92a7ba Mon Sep 17 00:00:00 2001 From: Jeff Morgan Date: Mon, 1 Sep 2014 15:55:13 -0700 Subject: [PATCH 05/13] Remove mongod.lock upon restart --- index.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/index.js b/index.js index 3a3d3a4989..9fe8b62b3e 100644 --- a/index.js +++ b/index.js @@ -43,10 +43,7 @@ var start = function (callback) { freeport(function(err, mongoPort) { console.log('MongoDB: ' + mongoPort); console.log('webPort: ' + webPort); - child_process.exec('kill $(ps aux -e | grep PURPOSE=KITEMATIC | awk \'{print $2}\')', function (error, stdout, stderr) { - console.log(error); - console.log(stdout); - console.log(stderr); + child_process.exec('kill $(ps aux -e | grep PURPOSE=KITEMATIC | awk \'{print $2}\') && rm ' + path.join(dataPath, 'mongod.lock'), function (error, stdout, stderr) { var mongoChild = child_process.spawn(path.join(process.cwd(), 'resources', 'mongod'), ['--bind_ip', '127.0.0.1', '--dbpath', dataPath, '--port', mongoPort, '--unixSocketPrefix', dataPath], { env: { PURPOSE: 'KITEMATIC' From dc1cb0caecdc415999ad3f22d83701949ffbf5d8 Mon Sep 17 00:00:00 2001 From: Jeff Morgan Date: Mon, 1 Sep 2014 17:19:19 -0700 Subject: [PATCH 06/13] Adding update process --- index.js | 5 ++++ meteor/client/lib/installer.js | 6 +++++ meteor/client/lib/router.js | 13 +++++++---- meteor/client/main.js | 2 +- .../views/dashboard/setup/setup-install.js | 2 +- .../views/dashboard/setup/setup-layout.html | 9 ++++++-- .../views/dashboard/setup/setup-layout.js | 11 +++++++++ meteor/collections/apps.js | 8 +++---- meteor/collections/installs.js | 23 +++++++++++++++++-- resources/kite-dns/dnsmasq.tmpl | 2 +- script/setup.sh | 6 +---- script/versions.sh | 2 +- 12 files changed, 68 insertions(+), 21 deletions(-) create mode 100644 meteor/client/views/dashboard/setup/setup-layout.js diff --git a/index.js b/index.js index 9fe8b62b3e..ba3a23f38f 100644 --- a/index.js +++ b/index.js @@ -51,7 +51,12 @@ var start = function (callback) { }); var started = false; mongoChild.stdout.setEncoding('utf8'); + mongoChild.stderr.setEncoding('utf8'); + mongoChild.stderr.on('data', function (data) { + console.log(data); + }); mongoChild.stdout.on('data', function (data) { + console.log(data); if (data.indexOf('waiting for connections on port ' + mongoPort)) { if (!started) { started = true; diff --git a/meteor/client/lib/installer.js b/meteor/client/lib/installer.js index 76a3465e0f..459ed06f7b 100644 --- a/meteor/client/lib/installer.js +++ b/meteor/client/lib/installer.js @@ -2,6 +2,12 @@ var async = require('async'); Installer = {}; +Installer.CURRENT_VERSION = '0.0.2'; + +Installer.isUpToDate = function () { + return !!Installs.findOne({version: Installer.CURRENT_VERSION}); +}; + /** * Install steps. A step is a function that accepts a function (err) callback and returns once that step is complete.keys: * - run: Function that runs the installation step and calls the callback with an error if failed. diff --git a/meteor/client/lib/router.js b/meteor/client/lib/router.js index 1ef66ba0dd..6e8168efd6 100755 --- a/meteor/client/lib/router.js +++ b/meteor/client/lib/router.js @@ -48,10 +48,15 @@ Router.map(function () { controller: 'SetupController', action: function () { if (this.ready()) { - var install = Installs.findOne(); - if (!install) { - console.log('No installs detected, running installer again.'); - this.redirect('/setup/intro'); + if (!Installer.isUpToDate()) { + if (!Installs.findOne()) { + console.log('No installs detected, running installer again.'); + this.redirect('/setup/intro'); + } else { + // There's an install but it's lower than the current version, re-run as an 'update'. + Session.set('isUpdating', true); + this.redirect('/setup/intro'); + } } else { this.redirect('/apps'); } diff --git a/meteor/client/main.js b/meteor/client/main.js index 0033c360b4..429201d295 100755 --- a/meteor/client/main.js +++ b/meteor/client/main.js @@ -146,7 +146,7 @@ Meteor.setInterval(function () { }, 5000); Meteor.setInterval(function () { - if (Installs.findOne()) { + if (Installer.isUpToDate()) { resolveWatchers(function () {}); if (!Session.get('boot2dockerOff')) { fixBoot2DockerVM(function (err) { diff --git a/meteor/client/views/dashboard/setup/setup-install.js b/meteor/client/views/dashboard/setup/setup-install.js index cc222f82e2..d5774fbbd7 100644 --- a/meteor/client/views/dashboard/setup/setup-install.js +++ b/meteor/client/views/dashboard/setup/setup-install.js @@ -7,7 +7,7 @@ Template.setup_install.rendered = function() { console.log('Setup failed.'); console.log(err); } else { - Installs.insert({}); + Installs.insert({version: Installer.CURRENT_VERSION}); Router.go('dashboard_apps'); } }); diff --git a/meteor/client/views/dashboard/setup/setup-layout.html b/meteor/client/views/dashboard/setup/setup-layout.html index 12c0f8682b..ce3765beb4 100644 --- a/meteor/client/views/dashboard/setup/setup-layout.html +++ b/meteor/client/views/dashboard/setup/setup-layout.html @@ -1,8 +1,13 @@ diff --git a/meteor/client/views/dashboard/setup/setup-layout.js b/meteor/client/views/dashboard/setup/setup-layout.js new file mode 100644 index 0000000000..2d99b3bc4b --- /dev/null +++ b/meteor/client/views/dashboard/setup/setup-layout.js @@ -0,0 +1,11 @@ +Template.setup_layout.rendered = function () { + Meteor.setInterval(function () { + $('.header .icons a').tooltip(); + }, 1000); +}; + +Template.setup_layout.helpers({ + isUpdating: function () { + return Session.get('isUpdating'); + } +}); diff --git a/meteor/collections/apps.js b/meteor/collections/apps.js index ba40c9b2e9..85cf5c5266 100755 --- a/meteor/collections/apps.js +++ b/meteor/collections/apps.js @@ -60,7 +60,7 @@ Apps.helpers({ return Images.findOne(this.imageId); }, hostUrl: function () { - return this.name + '.dev'; + return this.name + '.kite'; }, ports: function () { var app = this; @@ -78,7 +78,7 @@ Apps.helpers({ var app = this; var image = Images.findOne(app.imageId); if (image && image.meta.app && image.meta.app.webPort) { - return 'http://' + app.name + '.dev:' + image.meta.app.webPort; + return 'http://' + app.name + '.kite:' + image.meta.app.webPort; } else if (image && image.meta.app && image.meta.app.webPort === false) { return null; } else { @@ -93,14 +93,14 @@ Apps.helpers({ } }); if (pickedPort) { - return 'http://' + app.name + '.dev:' + pickedPort; + return 'http://' + app.name + '.kite:' + pickedPort; } else { if (keys.length > 0) { // Picks the first port that's not SSH for (var i = 0; i < keys.length; i++) { var port = parseInt(keys[i].split('/')[0], 10); if (port !== 22) { - return 'http://' + app.name + '.dev:' + port; + return 'http://' + app.name + '.kite:' + port; } } return null; diff --git a/meteor/collections/installs.js b/meteor/collections/installs.js index 6f0a492ce7..314b538050 100644 --- a/meteor/collections/installs.js +++ b/meteor/collections/installs.js @@ -1,7 +1,26 @@ Installs = new Meteor.Collection('installs'); schemaInstalls = new SimpleSchema({ - + createdAt: { + type: Date, + autoValue: function() { + var now = new Date(); + if (this.isInsert) { + return now; + } else if (this.isUpsert) { + return {$setOnInsert: now}; + } else { + this.unset(); + } + }, + denyUpdate: true, + label: 'Time of install created' + }, + version: { + type: String, + label: 'Installed version', + optional: true + } }); Installs.allow({ @@ -16,4 +35,4 @@ Installs.allow({ } }); -Installs.attachSchema(schemaInstalls); +Installs.attachSchema(schemaInstalls); \ No newline at end of file diff --git a/resources/kite-dns/dnsmasq.tmpl b/resources/kite-dns/dnsmasq.tmpl index 546fda7910..0355b257cc 100644 --- a/resources/kite-dns/dnsmasq.tmpl +++ b/resources/kite-dns/dnsmasq.tmpl @@ -1,6 +1,6 @@ user=root {{ range $index, $value := $ }} {{ with $address := index $value.Addresses 0 }} -address=/{{$value.Name}}.dev/{{$address.IP}} +address=/{{$value.Name}}.kite/{{$address.IP}} {{ end }} {{ end }} diff --git a/script/setup.sh b/script/setup.sh index c40c686e78..217e096da2 100755 --- a/script/setup.sh +++ b/script/setup.sh @@ -15,6 +15,7 @@ pushd cache if [ ! -f $BASE_IMAGE_VERSION_FILE ]; then cecho "-----> Downloading Kitematic base images..." $purple curl -L --progress-bar -o $BASE_IMAGE_VERSION_FILE https://s3.amazonaws.com/kite-installer/$BASE_IMAGE_VERSION_FILE + cp $BASE_IMAGE_VERSION_FILE ../resources/$BASE_IMAGE_FILE fi if [ ! -f $BOOT2DOCKER_CLI_VERSION_FILE ]; then @@ -60,11 +61,6 @@ if [ ! -f $COCOASUDO_FILE ]; then chmod +x $COCOASUDO_FILE fi - -if [ ! -f $BASE_IMAGE_FILE ]; then - cp ../cache/$BASE_IMAGE_VERSION_FILE $BASE_IMAGE_FILE -fi - cp ../cache/$BOOT2DOCKER_CLI_VERSION_FILE $BOOT2DOCKER_CLI_FILE chmod +x $BOOT2DOCKER_CLI_FILE diff --git a/script/versions.sh b/script/versions.sh index f1f7853a2f..e46149f22c 100644 --- a/script/versions.sh +++ b/script/versions.sh @@ -1,4 +1,4 @@ -BASE_IMAGE_VERSION=0.0.1 +BASE_IMAGE_VERSION=0.0.2 BASE_IMAGE_VERSION_FILE=base-images-$BASE_IMAGE_VERSION.tar.gz BASE_IMAGE_FILE=base-images.tar.gz From cc9b694e1fd6e02d615b547021c25af6c4492048 Mon Sep 17 00:00:00 2001 From: Jeff Morgan Date: Mon, 1 Sep 2014 18:11:52 -0700 Subject: [PATCH 07/13] Fixing launchagent to wait for our host-only network interface to be available before adding a route to the static routing table --- resources/install | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/resources/install b/resources/install index f558efe007..6f8f7c122f 100755 --- a/resources/install +++ b/resources/install @@ -21,15 +21,9 @@ echo " com.kitematic.route ProgramArguments - /sbin/route - -n - add - -net - 172.17.0.0 - -netmask - 255.255.0.0 - -gateway - $GATEWAY + bash + -c + /usr/sbin/scutil -w State:/Network/Interface/$IFACE/IPv4;/sbin/route -n add -net 172.17.0.0 -netmask 255.255.0.0 -gateway $GATEWAY KeepAlive From 8e3db7c30d6a1755279b8c77cae3356d35195100 Mon Sep 17 00:00:00 2001 From: Jeff Morgan Date: Mon, 1 Sep 2014 18:12:37 -0700 Subject: [PATCH 08/13] Linked --- meteor/client/lib/boot2docker.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meteor/client/lib/boot2docker.js b/meteor/client/lib/boot2docker.js index b1cb48837f..589b85b67d 100644 --- a/meteor/client/lib/boot2docker.js +++ b/meteor/client/lib/boot2docker.js @@ -32,7 +32,7 @@ Boot2Docker.stop = function (callback) { }; Boot2Docker.erase = function (callback) { - var VMFileLocation = path.join(getHomePath(), 'VirtualBox\\ VMs/boot2docker-vm'); + var VMFileLocation = path.join(Util.getHomePath(), 'VirtualBox\\ VMs/boot2docker-vm'); exec('rm -rf ' + VMFileLocation, function (err) { callback(err); }); @@ -85,7 +85,7 @@ Boot2Docker.start = function (callback) { self.injectUtilities(function (err) { callback(err); }); - }) + }); } else { callback(err); } From 6d50d91c08608b733d0857420b4717ffbd703aeb Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Mon, 1 Sep 2014 22:41:15 -0700 Subject: [PATCH 09/13] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c8cf734076..ff65bc23ee 100755 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Kitematic is still in Beta. Any effort in helping us find issues and improving t - Remove VirtualBox - rm /usr/local/bin/boot2docker -- sudo route delete 172.17.0.0/16 192.168.59.103 (disable routing to containers through VM) +- sudo route delete -net 172.17.0.0 -netmask 255.255.0.0 -gateway 192.168.60.103 (disable routing to containers through VM) - rm -rf ~/Library/Application\ Support/Kitematic (remove app data) - rm /Library/LaunchAgents/com.kitematic.route.plist (remove launch job that sets up routing to the containers) From f56acd75b84cb752fd2ca6a6239a549b0efa3b8b Mon Sep 17 00:00:00 2001 From: Jeff Morgan Date: Tue, 2 Sep 2014 00:00:40 -0700 Subject: [PATCH 10/13] Fixing installer bug if the route doesnt exist. --- resources/install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/install b/resources/install index 6f8f7c122f..cc67be5d49 100755 --- a/resources/install +++ b/resources/install @@ -35,5 +35,5 @@ echo " " > /Library/LaunchAgents/com.kitematic.route.plist # Add entries to routing table for Kitematic VM -/sbin/route delete -net 172.17.0.0 -netmask 255.255.0.0 -gateway $GATEWAY +/sbin/route delete -net 172.17.0.0 -netmask 255.255.0.0 -gateway $GATEWAY > /dev/null 2>&1 || true /sbin/route -n add -net 172.17.0.0 -netmask 255.255.0.0 -gateway $GATEWAY \ No newline at end of file From f5d76b600f02f7266750c83d3b6a418bf76c9936 Mon Sep 17 00:00:00 2001 From: Jeff Morgan Date: Tue, 2 Sep 2014 01:36:27 -0700 Subject: [PATCH 11/13] Fixing IFNAME variable in installer --- resources/install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/install b/resources/install index cc67be5d49..2d9b03c07c 100755 --- a/resources/install +++ b/resources/install @@ -23,7 +23,7 @@ echo " bash -c - /usr/sbin/scutil -w State:/Network/Interface/$IFACE/IPv4;/sbin/route -n add -net 172.17.0.0 -netmask 255.255.0.0 -gateway $GATEWAY + /usr/sbin/scutil -w State:/Network/Interface/$IFNAME/IPv4;/sbin/route -n add -net 172.17.0.0 -netmask 255.255.0.0 -gateway $GATEWAY KeepAlive From a9b68123382e4b81cd701fa9c12a0456f857034f Mon Sep 17 00:00:00 2001 From: Jeff Morgan Date: Tue, 2 Sep 2014 03:40:29 -0700 Subject: [PATCH 12/13] Fixing DNS issues, should have been .kite and not .dev --- meteor/server/apps.js | 2 +- meteor/server/docker.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/meteor/server/apps.js b/meteor/server/apps.js index a8e7c8dce7..d12a1e20a6 100755 --- a/meteor/server/apps.js +++ b/meteor/server/apps.js @@ -14,7 +14,7 @@ Apps.restart = function (app, callback) { }).run(); callback(null); // Use dig to refresh the DNS - exec('/usr/bin/dig dig ' + app.name + '.dev @172.17.42.1 ', function() {}); + exec('/usr/bin/dig dig ' + app.name + '.kite @172.17.42.1 ', function() {}); } else { callback(null); } diff --git a/meteor/server/docker.js b/meteor/server/docker.js index 37a27d6d88..6978024245 100755 --- a/meteor/server/docker.js +++ b/meteor/server/docker.js @@ -72,7 +72,7 @@ Docker.runContainer = function (app, image, callback) { if (err) { callback(err, null); return; } console.log('Started container: ' + container.id); // Use dig to refresh the DNS - exec('/usr/bin/dig dig ' + app.name + '.dev @172.17.42.1 ', function() {}); + exec('/usr/bin/dig dig ' + app.name + '.kite @172.17.42.1 ', function() {}); callback(null, container); }); }); From 965d7ac650702f7304b3c53cb1226f683e71db75 Mon Sep 17 00:00:00 2001 From: Sean Li Date: Tue, 2 Sep 2014 13:24:08 -0700 Subject: [PATCH 13/13] Updated Trello board link. --- CONTRIBUTING.md | 2 +- README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 45d12a3bca..2739e50077 100755 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -80,7 +80,7 @@ Example: Feature requests are welcome. But take a moment to find out whether your idea fits with the scope and aims of the project. A roadmap of the project is kept -on the [Kitematic Roadmap](https://trello.com/b/xea5AHRk/kitematic-roadmap) Trello board. +on the [Kitematic Roadmap](https://trello.com/b/G5Aw0Rqc/kitematic-roadmap) Trello board. It's up to *you* to make a strong case to convince the project's developers of the merits of this feature. Please provide as much detail and context as possible. diff --git a/README.md b/README.md index ff65bc23ee..ec66bc4084 100755 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ **Note:** If the installer gets stuck at any step for more than 1 minute, there is probably an error. Please help us troubleshoot by running it from the command line, and submit the logs to [contact@kitematic.com](mailto:contact@kitematic.com). 1. `cd ` -2. Run `./Kitematic.app/Contents/MacOS/node-webkit` +2. Run `./Kitematic.app/Contents/MacOS/node-webkit` Kitematic is still in Beta. Any effort in helping us find issues and improving the experience is greatly appreciated! @@ -61,7 +61,7 @@ Kitematic's documentation and other information can be found at [http://kitemati Please read through our [Contributing Guidelines](https://github.com/kitematic/kitematic/blob/master/CONTRIBUTING.md). Included are directions for opening issues, coding standards, and notes on development. -Development [Roadmap](https://trello.com/b/xea5AHRk/kitematic-roadmap) can be found on our Trello board. +Development [Roadmap](https://trello.com/b/G5Aw0Rqc/kitematic-roadmap) can be found on our Trello board. ## Community