diff --git a/README.md b/README.md index 750c0d812a..509b70b7d6 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # [Kitematic](https://kitematic.com) -![Kitematic Screenshot](http://kitematic.com/img/screenshot.0c17.png) +![Kitematic Screenshot](https://kitematic.com/img/screenshot.5843.png) ## Table of Contents diff --git a/meteor/client/lib/boot2docker.js b/meteor/client/lib/boot2docker.js index 62f898d473..65333e8240 100644 --- a/meteor/client/lib/boot2docker.js +++ b/meteor/client/lib/boot2docker.js @@ -2,7 +2,7 @@ var exec = require('exec'); var path = require('path'); boot2dockerexec = function (command, callback) { - exec(path.join(getBinDir(), 'boot2docker') + ' --lowerip=192.168.59.103 --upperip=192.168.59.103 --dhcp=false ' + command, function(err, stdout) { + exec(path.join(getBinDir(), 'boot2docker') + ' --lowerip=192.168.59.103 --upperip=192.168.59.103 ' + command, function(err, stdout) { callback(err, stdout); }); }; @@ -93,26 +93,30 @@ getBoot2DockerMemoryUsage = function (callback) { }; getBoot2DockerInfo = function (callback) { - getBoot2DockerState(function (err, state) { - if (err) { - callback(err, null); - return; - } - if (state === 'poweroff') { - callback(null, {state: state}); - } else { - getBoot2DockerMemoryUsage(function (err, mem) { - if (err) { callback(null, {state: state}); } - getBoot2DockerDiskUsage(function (err, disk) { + 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) { + if (err) { + callback(err, null); + return; + } + if (state === 'poweroff') { + callback(null, {state: state}); + } else { + getBoot2DockerMemoryUsage(function (err, mem) { if (err) { callback(null, {state: state}); } - callback(null, { - state: state, - memory: mem, - disk: disk + getBoot2DockerDiskUsage(function (err, disk) { + if (err) { callback(null, {state: state}); } + callback(null, { + state: state, + memory: mem, + disk: disk + }); }); }); - }); - } + } + }); + }); }); }; @@ -174,6 +178,8 @@ installBoot2DockerAddons = function (callback) { console.log(stdout); callback(err); }); + 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) { diff --git a/meteor/client/lib/sync.js b/meteor/client/lib/sync.js new file mode 100644 index 0000000000..19d24ffa9e --- /dev/null +++ b/meteor/client/lib/sync.js @@ -0,0 +1,118 @@ +var chokidar = require('chokidar'); +var path = require('path'); +var fs = require('fs'); +var child_process = require('child_process'); +var exec = require('exec'); + +var watchers = {}; + +removeAppWatcher = function (id) { + if (watchers[id]) { + watchers[id].watcher.close(); + delete watchers[id]; + } +}; + +addAppWatcher = function (app) { + removeAppWatcher(app._id); + var appPath = path.join(path.join(getHomePath(), 'Kitematic'), app.name); + var vmDir = path.join('/var/lib/docker/binds', app.name); + var vmPath = 'ssh://docker@localhost:2022/' + vmDir; + var watcher = chokidar.watch(appPath, {ignored: /.*\.DS_Store/}); + var syncing = false; + + var syncFunc = function (event, changedPath) { + if (syncing) { + return; + } + syncing = true; + // Make sure that if they delete the app_name folder under ~/Kitematic, we don't delete all the volumes. + // Deleting files inside the app_name folder _will_ delete the volumes. + + var errorPattern = /The\sfile\s(.*)\son\shost/g; + var archiveErrorPattern = /Archive\s(.*)\son\shost\s.*\sshould\sbe\sDELETED/g; + var cmd = path.join(getBinDir(), 'unison'); + var args = [ + cmd, + vmPath, + appPath, + '-prefer', + vmPath, + '-servercmd', + 'sudo\ unison', + '-batch', + '-log=false', + '-confirmbigdel=false', + '-ignore', + 'Name\ {*.tmp,*.unison,*.swp,*.pyc,.DS_Store}', + '-auto', + '-sshargs', + '-o\ UserKnownHostsFile=/dev/null\ -o\ StrictHostKeyChecking=no\ -o PreferredAuthentications=publickey\ -i\ ' + path.join(getHomePath(), '.ssh/id_boot2docker') + ]; + + if (!fs.existsSync(appPath)) { + args.push('-ignorearchives'); + console.log('Created Kite ' + app.name + ' directory.'); + fs.mkdirSync(appPath, function (err) { + if (err) { throw err; } + }); + } + + exec(args, function (err, out, code) { + try { + if (err.indexOf('the archives are locked.') !== -1) { + var results = errorPattern.exec(err); + var location = results[1].replace(' ', '\\ '); + exec('/bin/rm -rf ' + location, function () { + console.log('Removed unison file.'); + console.log(location); + }); + } + if (err.indexOf('The archive file is missing on some hosts') !== -1) { + var results = archiveErrorPattern.exec(err); + var location = results[1].replace(' ', '\\ '); + var fullLocation = path.join(getHomePath(), 'Library/Application\\ Support/Unison', location); + var cmd = '/bin/rm -rf ' + fullLocation; + exec(cmd, function () {}); + } + } catch (e) { + // console.error(e); + } + syncing = false; + }); + }; + + watchers[app._id] = { + watcher: watcher, + sync: syncFunc + }; + + // do a sync + syncFunc(); + watcher.on('all', syncFunc); +}; + +resolveWatchers = function (callback) { + var apps = Apps.find({}).fetch(); + var ids = _.map(apps, function(app) { + return app._id; + }); + var watcherKeys = _.keys(watchers); + var toAdd = _.difference(ids, watcherKeys); + var toRemove = _.difference(watcherKeys, ids); + + _.each(toAdd, function (id) { + addAppWatcher(Apps.findOne(id), function () {}); + }); + + _.each(toRemove, function (id) { + removeAppWatcher(id); + }); + + // Run a sync for 'pulling' changes in the volumes. + /*_.each(watchers, function (watcher) { + watcher.sync(); + });*/ + + callback(); +}; \ No newline at end of file diff --git a/meteor/client/lib/utilities.js b/meteor/client/lib/utilities.js index 90e8bda8b0..c3fe268798 100755 --- a/meteor/client/lib/utilities.js +++ b/meteor/client/lib/utilities.js @@ -4,7 +4,7 @@ getBinDir = function () { if (process.env.NODE_ENV === 'development') { return path.join(path.join(process.env.PWD, '..'), 'resources'); } else { - return path.join(path.join(process.cwd(), '../../..'), 'resources'); + return path.join(process.cwd(), 'resources'); } }; diff --git a/meteor/client/main.js b/meteor/client/main.js index 6e53b6d71c..7abe7d59a1 100755 --- a/meteor/client/main.js +++ b/meteor/client/main.js @@ -105,7 +105,7 @@ fixDefaultImages = function (callback) { Session.set('available', false); Meteor.call('resolveDefaultImages', function (err) { if (err) { - callback(err); + callback(); } else { Session.set('available', true); callback(); @@ -145,10 +145,10 @@ fixInterval = null; startFixInterval = function () { stopFixInterval(); fixInterval = Meteor.setInterval(function () { + resolveWatchers(function () {}); fixBoot2DockerVM(function (err) { if (err) { console.log(err); return; } - Meteor.call('resolveWatchers'); - Meteor.call('recoverApps'); + // Meteor.call('recoverApps'); fixDefaultImages(function (err) { if (err) { console.log(err); return; } fixDefaultContainers(function (err) { @@ -162,4 +162,4 @@ startFixInterval = function () { stopFixInterval = function () { Meteor.clearInterval(fixInterval); fixInterval = null; -}; +}; \ No newline at end of file diff --git a/meteor/client/views/dashboard/images/dashboard-images-settings.js b/meteor/client/views/dashboard/images/dashboard-images-settings.js index 9021d22a34..cfb1f8aa2d 100755 --- a/meteor/client/views/dashboard/images/dashboard-images-settings.js +++ b/meteor/client/views/dashboard/images/dashboard-images-settings.js @@ -7,6 +7,7 @@ Template.dashboard_images_settings.events({ $('#error-delete-image').html('' + err.reason + ''); $('#error-delete-image').fadeIn(); } else { + removeAppWatcher(this._id); Router.go('dashboard_images'); } }); diff --git a/meteor/client/views/dashboard/images/dashboard-single-image.html b/meteor/client/views/dashboard/images/dashboard-single-image.html index 427547d4ec..4040e6cdb7 100755 --- a/meteor/client/views/dashboard/images/dashboard-single-image.html +++ b/meteor/client/views/dashboard/images/dashboard-single-image.html @@ -30,7 +30,7 @@ {{/if}} - {{#if $eq status 'READY'}} + {{#if $neq status 'BUILDING'}} {{/if}} diff --git a/meteor/client/views/dashboard/layouts/dashboard-images-layout.html b/meteor/client/views/dashboard/layouts/dashboard-images-layout.html index 00cc525b7e..5200b750ba 100755 --- a/meteor/client/views/dashboard/layouts/dashboard-images-layout.html +++ b/meteor/client/views/dashboard/layouts/dashboard-images-layout.html @@ -15,7 +15,7 @@ {{/if}} - {{#if $eq status 'READY'}} + {{#if $neq status 'BUILDING'}} {{/if}} diff --git a/meteor/packages.json b/meteor/packages.json index 46e3409bd6..0de2f68fc2 100755 --- a/meteor/packages.json +++ b/meteor/packages.json @@ -2,6 +2,5 @@ "dockerode": "2.0.3", "tar": "0.1.20", "ansi-to-html": "0.2.0", - "async": "0.9.0", - "chokidar": "0.8.4" + "async": "0.9.0" } diff --git a/meteor/server/apps.js b/meteor/server/apps.js index 3f253afbd6..af9383afa3 100755 --- a/meteor/server/apps.js +++ b/meteor/server/apps.js @@ -1,100 +1,9 @@ -var watchers = {}; - removeBindFolder = function (name, callback) { exec(path.join(getBinDir(), 'boot2docker') + ' ssh "sudo rm -rf /var/lib/docker/binds/' + name + '"', function(err, stdout) { callback(err, stdout); }); }; -removeAppWatcher = function (id) { - if (watchers[id]) { - watchers[id].watcher.close(); - delete watchers[id]; - } -}; - -addAppWatcher = function (app) { - removeAppWatcher(app._id); - var appPath = path.join(KITE_PATH, app.name); - var vmDir = path.join('/var/lib/docker/binds', app.name); - var vmPath = 'ssh://docker@localhost:2022/' + vmDir; - var watcher = chokidar.watch(appPath, {ignored: /[\/\\]\./}); - - var syncFunc = function () { - // Make sure that if they delete the app_name folder under ~/Kitematic, we don't delete all the volumes. - // Deleting files inside the app_name folder _will_ delete the volumes. - var rootMissing = ''; - if (!fs.existsSync(appPath)) { - rootMissing = '-ignorearchives'; - console.log('Created Kite ' + app.name + ' directory.'); - fs.mkdirSync(appPath, function (err) { - if (err) { throw err; } - }); - } - - var errorPattern = /The\sfile\s(.*)\son\shost/g; - var archiveErrorPattern = /Archive\s(.*)\son\shost\s.*\sshould\sbe\sDELETED/g; - var command = path.join(getBinDir(), 'unison') + ' ' + vmPath + ' ' + appPath + ' -prefer ' + vmPath + ' ' + rootMissing + ' -servercmd "sudo unison" -batch -log=false -confirmbigdel=false -ignore "Name {*.tmp,*.unison,*.swp,*.pyc,.DS_STORE}" -auto -sshargs "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o PreferredAuthentications=publickey -i ' + path.join(getHomePath(), '.ssh/id_boot2docker') + '"'; - exec(command, function (err) { - if (err) { - var results; - var location; - console.error(err); - try { - if (err.message.indexOf('the archives are locked.') !== -1) { - results = errorPattern.exec(err.message); - location = results[1].replace(' ', '\\ '); - exec('/bin/rm -rf ' + location, function () { - console.log('Removed unison file.'); - console.log(location); - }); - } - if (err.message.indexOf('The archive file is missing on some hosts') !== -1) { - results = archiveErrorPattern.exec(err.message); - location = results[1].replace(' ', '\\ '); - var fullLocation = path.join(getHomePath(), 'Library/Application\\ Support/Unison', location); - var cmd = '/bin/rm -rf ' + fullLocation; - exec(cmd, function () {}); - } - } catch (e) { - console.error(e); - } - } - }); - }; - - watchers[app._id] = { - watcher: watcher, - sync: syncFunc - }; - watcher.on('all', syncFunc); -}; - -resolveWatchers = function (callback) { - var apps = Apps.find({}).fetch(); - var ids = _.map(apps, function(app) { - return app._id; - }); - var watcherKeys = _.keys(watchers); - var toAdd = _.difference(ids, watcherKeys); - var toRemove = _.difference(watcherKeys, ids); - - _.each(toAdd, function (id) { - addAppWatcher(Apps.findOne(id), function () {}); - }); - - _.each(toRemove, function (id) { - removeAppWatcher(id); - }); - - // Run a sync for 'pulling' changes in the volumes. - _.each(watchers, function (watcher) { - watcher.sync(); - }); - - callback(); -}; - recoverApps = function (callback) { var apps = Apps.find({}).fetch(); _.each(apps, function (app) { @@ -143,7 +52,6 @@ Meteor.methods({ if (err) { console.error(err); } var appPath = path.join(KITE_PATH, app.name); deleteFolder(appPath); - removeAppWatcher(app._id); removeBindFolder(app.name, function () { console.log('Deleted Kite ' + app.name + ' directory.'); Fiber(function () { diff --git a/meteor/server/docker.js b/meteor/server/docker.js index 2127ed8be4..e65c8026c8 100755 --- a/meteor/server/docker.js +++ b/meteor/server/docker.js @@ -94,9 +94,7 @@ 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(err, out, code) { - console.log(out); - }); + exec('/usr/bin/dig dig ' + app.name + '.dev @172.17.42.1 ', function(err, out, code) {}); callback(null, container); }); }); @@ -147,9 +145,7 @@ restartApp = function (app, callback) { callback(null); // Use dig to refresh the DNS - exec('/usr/bin/dig dig ' + app.name + '.dev @172.17.42.1 ', function(err, out, code) { - console.log(out); - }); + exec('/usr/bin/dig dig ' + app.name + '.dev @172.17.42.1 ', function(err, out, code) {}); } else { callback(null); } @@ -582,20 +578,29 @@ buildImage = function (image, callback) { console.error(e); } Fiber(function () { - var imageData = getImageDataSync(image._id); - var oldImageId = null; + try { + var imageData = getImageDataSync(image._id); + var oldImageId = null; + Images.update(image._id, { + $set: { + docker: imageData, + status: 'READY' + } + }); + } catch (e) { + console.log(e); + Images.update(image._id, { + $set: { + status: 'ERROR' + } + }); + } if (image.docker && image.docker.Id) { oldImageId = image.docker.Id; } if (oldImageId && oldImageId !== imageData.Id) { removeImageSync(oldImageId); } - Images.update(image._id, { - $set: { - docker: imageData, - status: 'READY' - } - }); }).run(); callback(null); }); diff --git a/meteor/server/images.js b/meteor/server/images.js index e7ab45ef39..5429dd53b1 100755 --- a/meteor/server/images.js +++ b/meteor/server/images.js @@ -137,9 +137,15 @@ Meteor.methods({ } var app = Apps.findOne({imageId: imageId}); if (!app) { - deleteImageSync(image); - deleteFolder(image.path); - Images.remove({_id: image._id}); + console.log('here'); + try { + deleteImageSync(image); + deleteFolder(image.path); + } catch (e) { + console.log(e); + } finally { + Images.remove({_id: image._id}); + } } else { throw new Meteor.Error(400, 'This image is currently being used by ' + app.name + "."); } diff --git a/meteor/server/lib/requires.js b/meteor/server/lib/requires.js index e4cbeaec97..cac98d255a 100755 --- a/meteor/server/lib/requires.js +++ b/meteor/server/lib/requires.js @@ -6,4 +6,4 @@ path = Meteor.require('path'); exec = Meteor.require('child_process').exec; async = Meteor.require('async'); Fiber = Meteor.require('fibers'); -chokidar = Meteor.require('chokidar'); +child_process = Meteor.require('child_process'); \ No newline at end of file diff --git a/meteor/server/lib/utilities.js b/meteor/server/lib/utilities.js index 9747fc139a..6b4d68df1a 100755 --- a/meteor/server/lib/utilities.js +++ b/meteor/server/lib/utilities.js @@ -6,7 +6,7 @@ getBinDir = function () { if (process.env.NODE_ENV === 'development') { return path.join(path.join(process.env.PWD, '..'), 'resources'); } else { - return path.join(path.join(process.cwd(), '../../..'), 'resources'); + return path.join(process.cwd(), '../../../resources'); } }; diff --git a/npm-debug.log b/npm-debug.log new file mode 100644 index 0000000000..e7412a0033 --- /dev/null +++ b/npm-debug.log @@ -0,0 +1,70 @@ +0 info it worked if it ends with ok +1 verbose cli [ '/Users/jmorgan/workspace/kite-desktop/script/../cache/node/bin/node', +1 verbose cli '/Users/jmorgan/workspace/kite-desktop/cache/node/bin/npm', +1 verbose cli 'install', +1 verbose cli '--quiet' ] +2 info using npm@1.4.14 +3 info using node@v0.10.29 +4 verbose node symlink /Users/jmorgan/workspace/kite-desktop/script/../cache/node/bin/node +5 warn package.json Kitematic@0.1.0 No repository field. +6 verbose readDependencies using package.json deps +7 verbose install where, deps [ '/Users/jmorgan/workspace/kite-desktop', +7 verbose install [ 'async', 'chokidar', 'exec', 'moment', 'open' ] ] +8 info preinstall Kitematic@0.1.0 +9 verbose readDependencies using package.json deps +10 verbose already installed skipping moment@2.8.1 /Users/jmorgan/workspace/kite-desktop +11 verbose already installed skipping open@0.0.5 /Users/jmorgan/workspace/kite-desktop +12 verbose already installed skipping exec@^0.1.2 /Users/jmorgan/workspace/kite-desktop +13 verbose already installed skipping async@^0.9.0 /Users/jmorgan/workspace/kite-desktop +14 verbose cache add [ 'chokidar@git+https://github.com/usekite/chokidar.git', null ] +15 verbose cache add name=undefined spec="chokidar@git+https://github.com/usekite/chokidar.git" args=["chokidar@git+https://github.com/usekite/chokidar.git",null] +16 verbose parsed url { protocol: null, +16 verbose parsed url slashes: null, +16 verbose parsed url auth: null, +16 verbose parsed url host: null, +16 verbose parsed url port: null, +16 verbose parsed url hostname: null, +16 verbose parsed url hash: null, +16 verbose parsed url search: null, +16 verbose parsed url query: null, +16 verbose parsed url pathname: 'chokidar@git+https://github.com/usekite/chokidar.git', +16 verbose parsed url path: 'chokidar@git+https://github.com/usekite/chokidar.git', +16 verbose parsed url href: 'chokidar@git+https://github.com/usekite/chokidar.git' } +17 verbose cache add name="chokidar" spec="git+https://github.com/usekite/chokidar.git" args=["chokidar","git+https://github.com/usekite/chokidar.git"] +18 verbose parsed url { protocol: 'git+https:', +18 verbose parsed url slashes: true, +18 verbose parsed url auth: null, +18 verbose parsed url host: 'github.com', +18 verbose parsed url port: null, +18 verbose parsed url hostname: 'github.com', +18 verbose parsed url hash: null, +18 verbose parsed url search: null, +18 verbose parsed url query: null, +18 verbose parsed url pathname: '/usekite/chokidar.git', +18 verbose parsed url path: '/usekite/chokidar.git', +18 verbose parsed url href: 'git+https://github.com/usekite/chokidar.git' } +19 silly lockFile 9e9e81d6--github-com-usekite-chokidar-git https://github.com/usekite/chokidar.git +20 verbose lock https://github.com/usekite/chokidar.git /Users/jmorgan/.npm/9e9e81d6--github-com-usekite-chokidar-git.lock +21 verbose addRemoteGit [ 'https://github.com/usekite/chokidar.git', 'master' ] +22 verbose git remote.origin.url https://github.com/usekite/chokidar.git +23 error git fetch -a origin (https://github.com/usekite/chokidar.git) fatal: unable to access 'https://github.com/usekite/chokidar.git/': Could not resolve host: github.com +24 silly lockFile 9e9e81d6--github-com-usekite-chokidar-git https://github.com/usekite/chokidar.git +25 silly lockFile 9e9e81d6--github-com-usekite-chokidar-git https://github.com/usekite/chokidar.git +26 error Error: Command failed: fatal: unable to access 'https://github.com/usekite/chokidar.git/': Could not resolve host: github.com +26 error +26 error at ChildProcess.exithandler (child_process.js:647:15) +26 error at ChildProcess.emit (events.js:98:17) +26 error at maybeClose (child_process.js:755:16) +26 error at Socket. (child_process.js:968:11) +26 error at Socket.emit (events.js:95:17) +26 error at Pipe.close (net.js:465:12) +27 error If you need help, you may report this *entire* log, +27 error including the npm and node versions, at: +27 error +28 error System Darwin 13.3.0 +29 error command "/Users/jmorgan/workspace/kite-desktop/script/../cache/node/bin/node" "/Users/jmorgan/workspace/kite-desktop/cache/node/bin/npm" "install" "--quiet" +30 error cwd /Users/jmorgan/workspace/kite-desktop +31 error node -v v0.10.29 +32 error npm -v 1.4.14 +33 error code 128 +34 verbose exit [ 1, true ] diff --git a/package.json b/package.json index 25897a59cc..62c09055d8 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "async": "^0.9.0", + "chokidar": "git+https://github.com/usekite/chokidar.git", "exec": "^0.1.2", "moment": "2.8.1", "open": "0.0.5" diff --git a/resources/install b/resources/install index 46b5a22bb1..ab0ae8eb4f 100755 --- a/resources/install +++ b/resources/install @@ -40,7 +40,7 @@ echo ' DIR=$(dirname "$0") USER=`w -h | sort -u -t' ' -k1,1 | awk '{print $1}'` -sudo -u $USER $DIR/boot2docker init +sudo -u $USER $DIR/boot2docker init --lowerip=192.168.59.103 --upperip=192.168.59.103 # Add entries to routing table for Kitematic VM /sbin/route delete 172.17.0.0/16 192.168.59.103 diff --git a/script/run.sh b/script/run.sh index 431511bb48..dd323ec117 100755 --- a/script/run.sh +++ b/script/run.sh @@ -7,8 +7,6 @@ export ROOT_URL=https://localhost:3000 export DOCKER_HOST=http://192.168.59.103 export DOCKER_PORT=2375 -$BASE/script/setup.sh - #export METEOR_SETTINGS=`cat $BASE/meteor/settings_dev.json` #echo $METEOR_SETTINGS