mirror of https://github.com/docker/docs.git
Merge pull request #27 from kitematic/installer_fix
Fixes issues with installer, now routes requests to .kite domains instead of .dev.
This commit is contained in:
commit
e0916a3d21
10
index.js
10
index.js
|
@ -43,10 +43,7 @@ var start = function (callback) {
|
||||||
freeport(function(err, mongoPort) {
|
freeport(function(err, mongoPort) {
|
||||||
console.log('MongoDB: ' + mongoPort);
|
console.log('MongoDB: ' + mongoPort);
|
||||||
console.log('webPort: ' + webPort);
|
console.log('webPort: ' + webPort);
|
||||||
child_process.exec('kill $(ps aux -e | grep PURPOSE=KITEMATIC | awk \'{print $2}\')', function (error, stdout, stderr) {
|
child_process.exec('kill $(ps aux -e | grep PURPOSE=KITEMATIC | awk \'{print $2}\') && rm ' + path.join(dataPath, 'mongod.lock'), function (error, stdout, stderr) {
|
||||||
console.log(error);
|
|
||||||
console.log(stdout);
|
|
||||||
console.log(stderr);
|
|
||||||
var mongoChild = child_process.spawn(path.join(process.cwd(), 'resources', 'mongod'), ['--bind_ip', '127.0.0.1', '--dbpath', dataPath, '--port', mongoPort, '--unixSocketPrefix', dataPath], {
|
var mongoChild = child_process.spawn(path.join(process.cwd(), 'resources', 'mongod'), ['--bind_ip', '127.0.0.1', '--dbpath', dataPath, '--port', mongoPort, '--unixSocketPrefix', dataPath], {
|
||||||
env: {
|
env: {
|
||||||
PURPOSE: 'KITEMATIC'
|
PURPOSE: 'KITEMATIC'
|
||||||
|
@ -54,7 +51,12 @@ var start = function (callback) {
|
||||||
});
|
});
|
||||||
var started = false;
|
var started = false;
|
||||||
mongoChild.stdout.setEncoding('utf8');
|
mongoChild.stdout.setEncoding('utf8');
|
||||||
|
mongoChild.stderr.setEncoding('utf8');
|
||||||
|
mongoChild.stderr.on('data', function (data) {
|
||||||
|
console.log(data);
|
||||||
|
});
|
||||||
mongoChild.stdout.on('data', function (data) {
|
mongoChild.stdout.on('data', function (data) {
|
||||||
|
console.log(data);
|
||||||
if (data.indexOf('waiting for connections on port ' + mongoPort)) {
|
if (data.indexOf('waiting for connections on port ' + mongoPort)) {
|
||||||
if (!started) {
|
if (!started) {
|
||||||
started = true;
|
started = true;
|
||||||
|
|
|
@ -128,7 +128,7 @@
|
||||||
// Packages
|
// Packages
|
||||||
"Fiber": true,
|
"Fiber": true,
|
||||||
"moment": true,
|
"moment": true,
|
||||||
"Docker": true,
|
"Dockerode": true,
|
||||||
"byline": true,
|
"byline": true,
|
||||||
"fs": true,
|
"fs": true,
|
||||||
"zlib": true,
|
"zlib": true,
|
||||||
|
@ -142,6 +142,9 @@
|
||||||
"chokidar": true,
|
"chokidar": true,
|
||||||
"docker": true,
|
"docker": true,
|
||||||
"async": true,
|
"async": true,
|
||||||
|
"child_process": true,
|
||||||
|
"convert": true,
|
||||||
|
"Convert": true,
|
||||||
|
|
||||||
// Collections
|
// Collections
|
||||||
"SimpleSchema": false,
|
"SimpleSchema": false,
|
||||||
|
@ -161,8 +164,13 @@
|
||||||
"SetupController": true,
|
"SetupController": true,
|
||||||
|
|
||||||
// Server and Client
|
// Server and Client
|
||||||
|
"Docker": true,
|
||||||
|
"Util": true,
|
||||||
|
"Boot2Docker": true,
|
||||||
|
"Installer": true,
|
||||||
|
"VirtualBox": true,
|
||||||
|
|
||||||
"boot2dockerexec": true,
|
"boot2dockerexec": true,
|
||||||
"getBinDir": true,
|
|
||||||
"getBoot2DockerIp": true,
|
"getBoot2DockerIp": true,
|
||||||
"getBoot2DockerState": true,
|
"getBoot2DockerState": true,
|
||||||
"getBoot2DockerDiskUsage": true,
|
"getBoot2DockerDiskUsage": true,
|
||||||
|
@ -170,7 +178,6 @@
|
||||||
"getBoot2DockerInfo": true,
|
"getBoot2DockerInfo": true,
|
||||||
"boot2DockerVMExists": true,
|
"boot2DockerVMExists": true,
|
||||||
"eraseBoot2DockerVMFiles": true,
|
"eraseBoot2DockerVMFiles": true,
|
||||||
"getHomePath": true,
|
|
||||||
"initBoot2Docker": true,
|
"initBoot2Docker": true,
|
||||||
"isVirtualBoxInstalled": true,
|
"isVirtualBoxInstalled": true,
|
||||||
"upgradeBoot2Docker": true,
|
"upgradeBoot2Docker": true,
|
||||||
|
@ -191,46 +198,17 @@
|
||||||
"fixInterval": true,
|
"fixInterval": true,
|
||||||
"stopFixInterval": true,
|
"stopFixInterval": true,
|
||||||
"runSetup": true,
|
"runSetup": true,
|
||||||
"removeBindFolder": true,
|
|
||||||
"removeAppWatcher": true,
|
"removeAppWatcher": true,
|
||||||
"addAppWatcher": true,
|
"addAppWatcher": true,
|
||||||
"resolveWatchers": true,
|
"resolveWatchers": true,
|
||||||
"recoverApps": true,
|
|
||||||
"restartApp": true,
|
|
||||||
"deleteApp": true,
|
|
||||||
"deleteFolder": true,
|
|
||||||
"loadKiteVolumes": true,
|
|
||||||
"getAppLogs": true,
|
|
||||||
"hasDockerfile": true,
|
|
||||||
"runContainer": true,
|
|
||||||
"runContainerSync": true,
|
|
||||||
"restartContainer": true,
|
|
||||||
"restartContainerSync": true,
|
|
||||||
"createTarFile": true,
|
|
||||||
"createTarFileSync": true,
|
|
||||||
"getImageData": true,
|
|
||||||
"getImageDataSync": true,
|
|
||||||
"removeImage": true,
|
|
||||||
"removeImageSync": true,
|
|
||||||
"deleteImage": true,
|
|
||||||
"checkDefaultImages": true,
|
"checkDefaultImages": true,
|
||||||
"resolveDefaultImages": true,
|
"resolveDefaultImages": true,
|
||||||
"checkDefaultContainers": true,
|
"checkDefaultContainers": true,
|
||||||
"resolveDefaultContainers": true,
|
"resolveDefaultContainers": true,
|
||||||
"killAndRemoveContainers": true,
|
"killAndRemoveContainers": true,
|
||||||
"deleteImageSync": true,
|
|
||||||
"upContainers": true,
|
"upContainers": true,
|
||||||
"reloadDefaultContainers": true,
|
"reloadDefaultContainers": true,
|
||||||
"removeImages": true,
|
"removeImages": true,
|
||||||
"pullImageFromDockerfile": true,
|
|
||||||
"buildImage": true,
|
|
||||||
"getImageMetaData": true,
|
|
||||||
"getImageJSON": true,
|
|
||||||
"rebuildImage": true,
|
|
||||||
"saveImageFolderSync": true,
|
|
||||||
"rebuildImageSync": true,
|
|
||||||
"saveImageFolder": true,
|
|
||||||
"copyFolder": true,
|
|
||||||
|
|
||||||
// Forms
|
// Forms
|
||||||
"showFormErrors": true,
|
"showFormErrors": true,
|
||||||
|
@ -239,11 +217,6 @@
|
||||||
"FormSchema": true,
|
"FormSchema": true,
|
||||||
"showFormSuccess": true,
|
"showFormSuccess": true,
|
||||||
"resetForm": true,
|
"resetForm": true,
|
||||||
"removeContainer": true,
|
|
||||||
"removeContainerSync": true,
|
|
||||||
"deleteAppSync": true,
|
|
||||||
"getContainerData": true,
|
|
||||||
"getContainerDataSync": true,
|
|
||||||
|
|
||||||
// Testing
|
// Testing
|
||||||
"require": false,
|
"require": false,
|
||||||
|
|
|
@ -1,14 +1,54 @@
|
||||||
var exec = require('exec');
|
var exec = require('exec');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
||||||
boot2dockerexec = function (command, callback) {
|
Boot2Docker = {};
|
||||||
exec(path.join(getBinDir(), 'boot2docker') + ' ' + command, function(err, stdout) {
|
|
||||||
callback(err, stdout);
|
Boot2Docker.REQUIRED_IP = '192.168.60.103';
|
||||||
|
|
||||||
|
Boot2Docker.exec = function (command, callback) {
|
||||||
|
exec(path.join(Util.getBinDir(), 'boot2docker') + ' ' + command, function(err, stdout, stderr) {
|
||||||
|
callback(err, stdout, stderr);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getBoot2DockerIp = function (callback) {
|
Boot2Docker.exists = function (callback) {
|
||||||
boot2dockerexec('ip', function (err, stdout) {
|
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(Util.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) {
|
if (err) {
|
||||||
callback(err, null);
|
callback(err, null);
|
||||||
} else {
|
} else {
|
||||||
|
@ -17,12 +57,54 @@ getBoot2DockerIp = function (callback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getBoot2DockerState = function (callback) {
|
Boot2Docker.setIp = function (ifname, ip, callback) {
|
||||||
boot2dockerexec(' info', function (err, stdout) {
|
this.exec('ssh "sudo ifconfig ' + ifname + ' ' + ip + ' netmask 255.255.255.0"', function (err, stdout) {
|
||||||
if (err) {
|
callback(err);
|
||||||
callback(err, null);
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
self.exec('up -v', function (err, stdout) {
|
||||||
|
// 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)) {
|
||||||
|
self.correct(function (err) {
|
||||||
|
if (err) { callback(err); return; }
|
||||||
|
self.injectUtilities(function (err) {
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
callback(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Boot2Docker.correct = function (callback) {
|
||||||
|
Boot2Docker.setIp('eth2', Boot2Docker.REQUIRED_IP, function(err) {
|
||||||
|
if (err) { callback(err); return; }
|
||||||
|
VirtualBox.removeDHCP(function (err) {
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Boot2Docker.state = function (callback) {
|
||||||
|
this.exec('info', function (err, stdout, stderr) {
|
||||||
|
if (err) { callback(err, null); return; }
|
||||||
try {
|
try {
|
||||||
var info = JSON.parse(stdout);
|
var info = JSON.parse(stdout);
|
||||||
callback(null, info.State);
|
callback(null, info.State);
|
||||||
|
@ -32,8 +114,8 @@ getBoot2DockerState = function (callback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getBoot2DockerDiskUsage = function (callback) {
|
Boot2Docker.diskUsage = function (callback) {
|
||||||
boot2dockerexec('ssh "df"', function (err, stdout) {
|
this.exec('ssh "df"', function (err, stdout) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err, null);
|
callback(err, null);
|
||||||
return;
|
return;
|
||||||
|
@ -61,8 +143,8 @@ getBoot2DockerDiskUsage = function (callback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getBoot2DockerMemoryUsage = function (callback) {
|
Boot2Docker.memoryUsage = function (callback) {
|
||||||
boot2dockerexec('ssh "free -m"', function (err, stdout) {
|
this.exec('ssh "free -m"', function (err, stdout) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err, null);
|
callback(err, null);
|
||||||
return;
|
return;
|
||||||
|
@ -92,178 +174,105 @@ getBoot2DockerMemoryUsage = function (callback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getBoot2DockerInfo = function (callback) {
|
Boot2Docker.stats = function (callback) {
|
||||||
boot2dockerexec('ssh "sudo ifconfig eth1 192.168.59.103 netmask 255.255.255.0"', function (err, stdout) {
|
var self = this;
|
||||||
exec('VBoxManage dhcpserver remove --netname HostInterfaceNetworking-vboxnet0', function (err, stdout) {
|
self.state(function (err, state) {
|
||||||
getBoot2DockerState(function (err, state) {
|
if (err) { callback(err, null); return; }
|
||||||
|
if (state === 'poweroff') {
|
||||||
|
callback(null, {state: state});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.memoryUsage(function (err, mem) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err, null);
|
callback(null, {state: state});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (state === 'poweroff') {
|
self.diskUsage(function (err, disk) {
|
||||||
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);
|
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.indexOf('exit status 1') !== -1) {
|
callback(null, {state: state, memory: mem});
|
||||||
eraseBoot2DockerVMFiles(function () {
|
return;
|
||||||
boot2dockerexec('init', function (err) {
|
|
||||||
callback(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
callback(err);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
}
|
||||||
|
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 {
|
} else {
|
||||||
callback(new Error('initBoot2Docker called but VirtualBox isn\'t installed.'));
|
callback(null, stdout);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
upgradeBoot2Docker = function (callback) {
|
Boot2Docker.version = function (callback) {
|
||||||
boot2dockerexec('upgrade', function (err, stdout) {
|
this.exec('version', function (err, stdout, stderr) {
|
||||||
console.log(stdout);
|
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]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Boot2Docker.injectUtilities = function (callback) {
|
||||||
|
exec('/bin/cat ' + path.join(Util.getBinDir(), 'kite-binaries.tar.gz') + ' | ' + path.join(Util.getBinDir(), 'boot2docker') + ' ssh "tar zx -C /usr/local/bin"', function (err, stdout) {
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
installBoot2DockerAddons = function (callback) {
|
Boot2Docker.check = 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) {
|
var self = this;
|
||||||
console.log(stdout);
|
self.exists(function (err, exists) {
|
||||||
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) {
|
|
||||||
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) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
getBoot2DockerState(function (err, state) {
|
self.state(function (err, state) {
|
||||||
if (state !== 'running') {
|
if (state !== 'running') {
|
||||||
callback('boot2docker not running');
|
callback('boot2docker not running');
|
||||||
} else {
|
} else {
|
||||||
callback();
|
self.correct(function (err) {
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Make sure the VM exists, is up and is running.
|
Boot2Docker.resolve = function (callback) {
|
||||||
resolveBoot2DockerVM = function (callback) {
|
var self = this;
|
||||||
boot2DockerVMExists(function (err, exists) {
|
self.exists(function (err, exists) {
|
||||||
|
|
||||||
// If somehow the boot2docker VM doesn't exist anymor then re-create it.
|
// If somehow the boot2docker VM doesn't exist anymor then re-create it.
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
initBoot2Docker(function () {
|
self.init(function () {
|
||||||
startBoot2Docker(function (err) {
|
self.start(function (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// If it exists but it's not running.. restart it.
|
// If it exists but it's not running.. restart it.
|
||||||
getBoot2DockerState(function (err, state) {
|
self.state(function (err, state) {
|
||||||
if (state !== 'running') {
|
if (state !== 'running') {
|
||||||
startBoot2Docker(function (err) {
|
self.start(function (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
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.
|
||||||
|
* - 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 = Util.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) {
|
||||||
|
if (err) { callback(err); return; }
|
||||||
|
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)'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
run: function (callback) {
|
||||||
|
VirtualBox.addCustomHostAdapter('boot2docker-vm', function (err, ifname) {
|
||||||
|
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'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Boot2Docker.setIp('eth2', Boot2Docker.REQUIRED_IP, function(err) {
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
pastMessage: 'Started the Boot2Docker VM',
|
||||||
|
message: 'Starting the Boot2Docker VM',
|
||||||
|
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) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,2 @@
|
||||||
|
path = require('path');
|
||||||
|
fs = require('fs');
|
|
@ -48,12 +48,16 @@ Router.map(function () {
|
||||||
controller: 'SetupController',
|
controller: 'SetupController',
|
||||||
action: function () {
|
action: function () {
|
||||||
if (this.ready()) {
|
if (this.ready()) {
|
||||||
var install = Installs.findOne();
|
if (!Installer.isUpToDate()) {
|
||||||
if (!install) {
|
if (!Installs.findOne()) {
|
||||||
console.log('No installs detected, running installer again.');
|
console.log('No installs detected, running installer again.');
|
||||||
this.redirect('/setup/intro');
|
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 {
|
} else {
|
||||||
startFixInterval();
|
|
||||||
this.redirect('/apps');
|
this.redirect('/apps');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ removeAppWatcher = function (id) {
|
||||||
|
|
||||||
addAppWatcher = function (app) {
|
addAppWatcher = function (app) {
|
||||||
removeAppWatcher(app._id);
|
removeAppWatcher(app._id);
|
||||||
var appPath = path.join(path.join(getHomePath(), 'Kitematic'), app.name);
|
var appPath = path.join(path.join(Util.getHomePath(), 'Kitematic'), app.name);
|
||||||
var vmDir = path.join('/var/lib/docker/binds', app.name);
|
var vmDir = path.join('/var/lib/docker/binds', app.name);
|
||||||
var vmPath = 'ssh://docker@localhost:2022/' + vmDir;
|
var vmPath = 'ssh://docker@localhost:2022/' + vmDir;
|
||||||
var watcher = chokidar.watch(appPath, {ignored: /.*\.DS_Store/});
|
var watcher = chokidar.watch(appPath, {ignored: /.*\.DS_Store/});
|
||||||
|
@ -30,7 +30,7 @@ addAppWatcher = function (app) {
|
||||||
syncing = true;
|
syncing = true;
|
||||||
var errorPattern = /The\sfile\s(.*)\son\shost/g;
|
var errorPattern = /The\sfile\s(.*)\son\shost/g;
|
||||||
var archiveErrorPattern = /Archive\s(.*)\son\shost\s.*\sshould\sbe\sDELETED/g;
|
var archiveErrorPattern = /Archive\s(.*)\son\shost\s.*\sshould\sbe\sDELETED/g;
|
||||||
var cmd = path.join(getBinDir(), 'unison');
|
var cmd = path.join(Util.getBinDir(), 'unison');
|
||||||
var args = [
|
var args = [
|
||||||
cmd,
|
cmd,
|
||||||
vmPath,
|
vmPath,
|
||||||
|
@ -46,7 +46,7 @@ addAppWatcher = function (app) {
|
||||||
'Name\ {*.tmp,*.unison,*.swp,*.pyc,.DS_Store}',
|
'Name\ {*.tmp,*.unison,*.swp,*.pyc,.DS_Store}',
|
||||||
'-auto',
|
'-auto',
|
||||||
'-sshargs',
|
'-sshargs',
|
||||||
'-o\ UserKnownHostsFile=/dev/null\ -o\ StrictHostKeyChecking=no\ -o PreferredAuthentications=publickey\ -i\ ' + path.join(getHomePath(), '.ssh/id_boot2docker')
|
'-o\ UserKnownHostsFile=/dev/null\ -o\ StrictHostKeyChecking=no\ -o PreferredAuthentications=publickey\ -i\ ' + path.join(Util.getHomePath(), '.ssh/id_boot2docker')
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!fs.existsSync(appPath)) {
|
if (!fs.existsSync(appPath)) {
|
||||||
|
@ -70,12 +70,12 @@ addAppWatcher = function (app) {
|
||||||
if (err.indexOf('The archive file is missing on some hosts') !== -1) {
|
if (err.indexOf('The archive file is missing on some hosts') !== -1) {
|
||||||
var results = archiveErrorPattern.exec(err);
|
var results = archiveErrorPattern.exec(err);
|
||||||
var location = results[1].replace(' ', '\\ ');
|
var location = results[1].replace(' ', '\\ ');
|
||||||
var fullLocation = path.join(getHomePath(), 'Library/Application\\ Support/Unison', location);
|
var fullLocation = path.join(Util.getHomePath(), 'Library/Application\\ Support/Unison', location);
|
||||||
var cmd = '/bin/rm -rf ' + fullLocation;
|
var cmd = '/bin/rm -rf ' + fullLocation;
|
||||||
exec(cmd, function () {});
|
exec(cmd, function () {});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// console.error(e);
|
//console.error(e);
|
||||||
}
|
}
|
||||||
syncing = false;
|
syncing = false;
|
||||||
if (willSyncAgain) {
|
if (willSyncAgain) {
|
||||||
|
@ -118,4 +118,4 @@ resolveWatchers = function (callback) {
|
||||||
});
|
});
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,17 +1,3 @@
|
||||||
var path = require('path');
|
|
||||||
|
|
||||||
getBinDir = function () {
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
return path.join(path.join(process.env.PWD, '..'), 'resources');
|
|
||||||
} else {
|
|
||||||
return path.join(process.cwd(), 'resources');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
getHomePath = function () {
|
|
||||||
return process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];
|
|
||||||
};
|
|
||||||
|
|
||||||
showFormErrors = function ($form, errors) {
|
showFormErrors = function ($form, errors) {
|
||||||
for (var name in errors) {
|
for (var name in errors) {
|
||||||
if (errors.hasOwnProperty(name)) {
|
if (errors.hasOwnProperty(name)) {
|
||||||
|
@ -31,7 +17,7 @@ clearFormErrors = function ($form) {
|
||||||
$form.find('.form-group.has-error .help-block.error').remove();
|
$form.find('.form-group.has-error .help-block.error').remove();
|
||||||
$form.find('.form-group.has-error').removeClass('has-error');
|
$form.find('.form-group.has-error').removeClass('has-error');
|
||||||
};
|
};
|
||||||
|
|
||||||
resetForm = function ($form) {
|
resetForm = function ($form) {
|
||||||
$form.find('input').val('');
|
$form.find('input').val('');
|
||||||
};
|
};
|
||||||
|
@ -41,4 +27,4 @@ trackLink = function (trackLabel) {
|
||||||
console.log(trackLabel);
|
console.log(trackLabel);
|
||||||
ga('send', 'event', 'link', 'click', trackLabel);
|
ga('send', 'event', 'link', 'click', trackLabel);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,55 +1,158 @@
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var child_process = require('child_process');
|
var exec = require('exec');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
||||||
isVirtualBoxInstalled = function (callback) {
|
VirtualBox = {};
|
||||||
fs.exists('/usr/bin/VBoxManage', function (exists) {
|
|
||||||
callback(null, exists);
|
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) {
|
VirtualBox.install = function (callback) {
|
||||||
fs.readFile('/etc/resolver/dev', {
|
// -W waits for the process to close before finishing.
|
||||||
encoding: 'utf8'
|
exec('open -W ' + path.join(Util.getBinDir(), this.INSTALLER_FILENAME), function (error, stdout, stderr) {
|
||||||
}, function (err, data) {
|
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) {
|
if (err) {
|
||||||
callback(err, false);
|
callback(err);
|
||||||
} else {
|
return;
|
||||||
if (data.indexOf('nameserver 172.17.42.1') !== -1) {
|
}
|
||||||
callback(null, true);
|
// Output is x.x.xryyyyyy
|
||||||
} else {
|
var match = stdout.match(/(\d+\.\d+\.\d+).*/);
|
||||||
callback(null, false);
|
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) {
|
VirtualBox.hostOnlyAdapters = function (vm, callback) {
|
||||||
var installFile = path.join(getBinDir(), 'install');
|
this.exec('showvminfo ' + vm + ' --machinereadable', function (err, stdout, stderr) {
|
||||||
var cocoaSudo = path.join(getBinDir(), 'cocoasudo');
|
if (err) {
|
||||||
var execCommand = cocoaSudo + ' --prompt="Kitematic Setup wants to make changes. Type your password to allow this." ' + installFile;
|
callback(err);
|
||||||
child_process.exec(execCommand, function (error, stdout, stderr) {
|
|
||||||
console.log(stdout);
|
|
||||||
if (error) {
|
|
||||||
console.log(error);
|
|
||||||
callback(error);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('Virtualbox Installation & Resolver config complete.');
|
var matches = stdout.match(/(hostonlyadapter\d+)="(vboxnet\d+)"/g);
|
||||||
callback();
|
if (!matches.length) {
|
||||||
});
|
callback(null, {});
|
||||||
};
|
} else {
|
||||||
|
var objs = {};
|
||||||
setupVirtualBox = function (callback) {
|
_.each(matches, function (match) {
|
||||||
child_process.exec('open -W ' + path.join(getBinDir(), 'virtualbox-4.3.12.pkg'), function (error, stdout, stderr) {
|
var pieces = match.split('=');
|
||||||
console.log(stdout);
|
objs[pieces[0]] = pieces[1].replace(/"/g, '');
|
||||||
if (error) {
|
});
|
||||||
console.log(error);
|
callback(null, objs);
|
||||||
callback(error);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
console.log('Virtualbox Installation running.');
|
|
||||||
callback();
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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/);
|
||||||
|
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; }
|
||||||
|
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(Util.getBinDir(), 'install');
|
||||||
|
var cocoaSudo = path.join(Util.getBinDir(), 'cocoasudo');
|
||||||
|
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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
VirtualBox.removeDHCP = function (callback) {
|
||||||
|
var self = this;
|
||||||
|
self.hostOnlyAdapter(function (err, ifname) {
|
||||||
|
if (err) { callback(err); return; }
|
||||||
|
self.exec('dhcpserver remove --ifname ' + ifname, function (err, stdout, stderr) {
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
@ -68,24 +68,11 @@ Meteor.call('getDockerHost', function (err, host) {
|
||||||
Session.set('dockerHost', 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) {
|
fixBoot2DockerVM = function (callback) {
|
||||||
checkBoot2DockerVM(function (err) {
|
Boot2Docker.check(function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
Session.set('available', false);
|
Session.set('available', false);
|
||||||
resolveBoot2DockerVM(function (err) {
|
Boot2Docker.resolve(function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
} else {
|
} else {
|
||||||
|
@ -138,28 +125,41 @@ fixDefaultContainers = function (callback) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Meteor.setInterval(function () {
|
Meteor.setInterval(function () {
|
||||||
updateBoot2DockerInfo();
|
Boot2Docker.exists(function (err, exists) {
|
||||||
|
if (err) { console.log(err); return; }
|
||||||
|
if (exists) {
|
||||||
|
Boot2Docker.state(function (err, state) {
|
||||||
|
if (err) { console.log(err); return; }
|
||||||
|
Session.set('boot2dockerState', state);
|
||||||
|
if (state === 'running') {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
fixInterval = null;
|
Meteor.setInterval(function () {
|
||||||
startFixInterval = function () {
|
if (Installer.isUpToDate()) {
|
||||||
stopFixInterval();
|
|
||||||
fixInterval = Meteor.setInterval(function () {
|
|
||||||
resolveWatchers(function () {});
|
resolveWatchers(function () {});
|
||||||
fixBoot2DockerVM(function (err) {
|
if (!Session.get('boot2dockerOff')) {
|
||||||
if (err) { console.log(err); return; }
|
fixBoot2DockerVM(function (err) {
|
||||||
// Meteor.call('recoverApps');
|
|
||||||
fixDefaultImages(function (err) {
|
|
||||||
if (err) { console.log(err); return; }
|
if (err) { console.log(err); return; }
|
||||||
fixDefaultContainers(function (err) {
|
Meteor.call('recoverApps');
|
||||||
if (err) { console.log(err); }
|
fixDefaultImages(function (err) {
|
||||||
|
if (err) { console.log(err); return; }
|
||||||
|
fixDefaultContainers(function (err) {
|
||||||
|
if (err) { console.log(err); }
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}, 5000);
|
}
|
||||||
};
|
}, 5000);
|
||||||
|
|
||||||
stopFixInterval = function () {
|
|
||||||
Meteor.clearInterval(fixInterval);
|
|
||||||
fixInterval = null;
|
|
||||||
};
|
|
|
@ -21,8 +21,8 @@ Template.dashboard_single_app.events({
|
||||||
},
|
},
|
||||||
'click .btn-terminal': function () {
|
'click .btn-terminal': function () {
|
||||||
var app = this;
|
var app = this;
|
||||||
var cmd = path.join(getBinDir(), 'boot2docker') + ' ssh -t "sudo docker-enter ' + app.docker.Id + '"';
|
var cmd = path.join(Util.getBinDir(), 'boot2docker') + ' ssh -t "sudo docker-enter ' + app.docker.Id + '"';
|
||||||
var terminalCmd = path.join(getBinDir(), 'terminal') + ' ' + cmd;
|
var terminalCmd = path.join(Util.getBinDir(), 'terminal') + ' ' + cmd;
|
||||||
var exec = require('child_process').exec;
|
var exec = require('child_process').exec;
|
||||||
console.log(terminalCmd);
|
console.log(terminalCmd);
|
||||||
exec(terminalCmd, function (err, stdout) {
|
exec(terminalCmd, function (err, stdout) {
|
||||||
|
|
|
@ -3,18 +3,22 @@ Template.dashboard_settings.events({
|
||||||
var $btn = $(e.currentTarget);
|
var $btn = $(e.currentTarget);
|
||||||
$btn.html('Starting Boot2Docker...');
|
$btn.html('Starting Boot2Docker...');
|
||||||
$btn.attr("disabled", "disabled");
|
$btn.attr("disabled", "disabled");
|
||||||
startFixInterval();
|
Session.set('boot2dockerOff', false);
|
||||||
startBoot2Docker(function (err) {
|
Boot2Docker.start(function (err) {
|
||||||
if (err) { console.error(err); }
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'click .btn-stop-boot2docker': function (e) {
|
'click .btn-stop-boot2docker': function (e) {
|
||||||
var $btn = $(e.currentTarget);
|
var $btn = $(e.currentTarget);
|
||||||
$btn.html('Stopping Boot2Docker...');
|
$btn.html('Stopping Boot2Docker...');
|
||||||
$btn.attr("disabled", "disabled");
|
$btn.attr("disabled", "disabled");
|
||||||
stopFixInterval();
|
Session.set('boot2dockerOff', true);
|
||||||
stopBoot2Docker(function (err) {
|
Boot2Docker.stop(function (err) {
|
||||||
if (err) { console.error(err); }
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,142 +1,13 @@
|
||||||
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;
|
var installStarted = false;
|
||||||
Template.setup_install.rendered = function() {
|
Template.setup_install.rendered = function() {
|
||||||
if(!installStarted) {
|
if(!installStarted) {
|
||||||
installStarted = true;
|
installStarted = true;
|
||||||
runSetup(function (err) {
|
Installer.run(function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log('Setup failed.');
|
console.log('Setup failed.');
|
||||||
console.log(err);
|
console.log(err);
|
||||||
} else {
|
} else {
|
||||||
Installs.insert({});
|
Installs.insert({version: Installer.CURRENT_VERSION});
|
||||||
startFixInterval();
|
|
||||||
Router.go('dashboard_apps');
|
Router.go('dashboard_apps');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -144,7 +15,7 @@ Template.setup_install.rendered = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
Template.setup_install.steps = function () {
|
Template.setup_install.steps = function () {
|
||||||
return steps.map(function (step, index) {
|
return Installer.steps.map(function (step, index) {
|
||||||
step.index = index;
|
step.index = index;
|
||||||
return step;
|
return step;
|
||||||
});
|
});
|
||||||
|
@ -155,7 +26,7 @@ Template.setup_install.helpers({
|
||||||
return Session.get('currentInstallStep');
|
return Session.get('currentInstallStep');
|
||||||
},
|
},
|
||||||
installComplete: function () {
|
installComplete: function () {
|
||||||
return Session.get('currentInstallStep') === steps.length;
|
return Session.get('currentInstallStep') === Installer.steps.length;
|
||||||
},
|
},
|
||||||
failedStep: function () {
|
failedStep: function () {
|
||||||
return Session.get('failedStep');
|
return Session.get('failedStep');
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
<template name="setup_layout">
|
<template name="setup_layout">
|
||||||
{{setTitle}}
|
{{setTitle}}
|
||||||
<div class="setup content text-center">
|
<div class="setup content text-center">
|
||||||
<h2>Welcome to Kitematic</h2>
|
{{#if isUpdating}}
|
||||||
<p>This will set up everything needed to run Kitematic on your Mac.</p>
|
<h2>Welcome Back</h2>
|
||||||
|
<p>Kitematic needs to update itself to continue.</p>
|
||||||
|
{{else}}
|
||||||
|
<h2>Welcome to Kitematic</h2>
|
||||||
|
<p>This will set up everything needed to run Kitematic on your Mac.</p>
|
||||||
|
{{/if}}
|
||||||
{{> yield}}
|
{{> yield}}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -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');
|
||||||
|
}
|
||||||
|
});
|
|
@ -60,7 +60,7 @@ Apps.helpers({
|
||||||
return Images.findOne(this.imageId);
|
return Images.findOne(this.imageId);
|
||||||
},
|
},
|
||||||
hostUrl: function () {
|
hostUrl: function () {
|
||||||
return this.name + '.dev';
|
return this.name + '.kite';
|
||||||
},
|
},
|
||||||
ports: function () {
|
ports: function () {
|
||||||
var app = this;
|
var app = this;
|
||||||
|
@ -78,7 +78,7 @@ Apps.helpers({
|
||||||
var app = this;
|
var app = this;
|
||||||
var image = Images.findOne(app.imageId);
|
var image = Images.findOne(app.imageId);
|
||||||
if (image && image.meta.app && image.meta.app.webPort) {
|
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) {
|
} else if (image && image.meta.app && image.meta.app.webPort === false) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
|
@ -93,14 +93,14 @@ Apps.helpers({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (pickedPort) {
|
if (pickedPort) {
|
||||||
return 'http://' + app.name + '.dev:' + pickedPort;
|
return 'http://' + app.name + '.kite:' + pickedPort;
|
||||||
} else {
|
} else {
|
||||||
if (keys.length > 0) {
|
if (keys.length > 0) {
|
||||||
// Picks the first port that's not SSH
|
// Picks the first port that's not SSH
|
||||||
for (var i = 0; i < keys.length; i++) {
|
for (var i = 0; i < keys.length; i++) {
|
||||||
var port = parseInt(keys[i].split('/')[0], 10);
|
var port = parseInt(keys[i].split('/')[0], 10);
|
||||||
if (port !== 22) {
|
if (port !== 22) {
|
||||||
return 'http://' + app.name + '.dev:' + port;
|
return 'http://' + app.name + '.kite:' + port;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -116,3 +116,41 @@ Apps.helpers({
|
||||||
});
|
});
|
||||||
|
|
||||||
Apps.attachSchema(schemaApps);
|
Apps.attachSchema(schemaApps);
|
||||||
|
|
||||||
|
Apps.after.insert(function (userId, app) {
|
||||||
|
// Give app an unique environment variable
|
||||||
|
var appId = this._id;
|
||||||
|
Apps.update(appId, {
|
||||||
|
$set: {
|
||||||
|
'config.APP_ID': appId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var image = Images.findOne(app.imageId);
|
||||||
|
Util.copyVolumes(image.path, app.name);
|
||||||
|
app = Apps.findOne(appId);
|
||||||
|
Docker.removeBindFolder(app.name, function (err) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
Fiber(function () {
|
||||||
|
Meteor.call('runApp', app, function (err) {
|
||||||
|
if (err) { throw err; }
|
||||||
|
});
|
||||||
|
}).run();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Apps.after.remove(function (userId, app) {
|
||||||
|
if (app.docker) {
|
||||||
|
try {
|
||||||
|
Docker.removeContainerSync(app.docker.Id);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var appPath = path.join(KITE_PATH, app.name);
|
||||||
|
Util.deleteFolder(appPath);
|
||||||
|
Docker.removeBindFolder(app.name, function () {
|
||||||
|
console.log('Deleted Kite ' + app.name + ' directory.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -91,3 +91,43 @@ Images.allow({
|
||||||
});
|
});
|
||||||
|
|
||||||
Images.attachSchema(schemaImages);
|
Images.attachSchema(schemaImages);
|
||||||
|
|
||||||
|
Images.after.insert(function (userId, image) {
|
||||||
|
var imageId = this._id;
|
||||||
|
var imagePath = path.join(KITE_IMAGES_PATH, imageId);
|
||||||
|
Images.update(imageId, {
|
||||||
|
$set: {
|
||||||
|
path: imagePath
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (image.meta.logo) {
|
||||||
|
Images.update(imageId, {
|
||||||
|
$set: {
|
||||||
|
logoPath: path.join(imagePath, image.meta.logo)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
image = Images.findOne(imageId);
|
||||||
|
Images.saveFolderSync(image.originPath, imageId);
|
||||||
|
Images.pull(fs.readFileSync(path.join(image.path, 'Dockerfile'), 'utf8'), imageId, function (err) {
|
||||||
|
if (err) { throw err; }
|
||||||
|
Images.build(image, function (err) {
|
||||||
|
if (err) { console.error(err); }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Images.after.remove(function (userId, image) {
|
||||||
|
if (image.docker) {
|
||||||
|
try {
|
||||||
|
Docker.removeImageSync(image.docker.Id);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Util.deleteFolder(image.path);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -1,7 +1,26 @@
|
||||||
Installs = new Meteor.Collection('installs');
|
Installs = new Meteor.Collection('installs');
|
||||||
|
|
||||||
schemaInstalls = new SimpleSchema({
|
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({
|
Installs.allow({
|
||||||
|
@ -16,4 +35,4 @@ Installs.allow({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Installs.attachSchema(schemaInstalls);
|
Installs.attachSchema(schemaInstalls);
|
|
@ -0,0 +1,146 @@
|
||||||
|
Util = {};
|
||||||
|
|
||||||
|
Util.getHomePath = function () {
|
||||||
|
return process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];
|
||||||
|
};
|
||||||
|
|
||||||
|
Util.getBinDir = function () {
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
return path.join(path.join(process.env.PWD, '..'), 'resources');
|
||||||
|
} else {
|
||||||
|
if (Meteor.isClient) {
|
||||||
|
return path.join(process.cwd(), 'resources');
|
||||||
|
} else {
|
||||||
|
return path.join(process.cwd(), '../../../resources');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Util.deleteFolder = function (directory) {
|
||||||
|
if (fs.existsSync(directory)) {
|
||||||
|
fs.readdirSync(directory).forEach(function (file) {
|
||||||
|
var curDirectory = directory + '/' + file;
|
||||||
|
if (fs.lstatSync(curDirectory).isDirectory()) {
|
||||||
|
// Recurse
|
||||||
|
Util.deleteFolder(curDirectory);
|
||||||
|
} else {
|
||||||
|
// Delete File
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(curDirectory);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fs.rmdirSync(directory);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Util.copyFolder = function (src, dest) {
|
||||||
|
var exists = fs.existsSync(src);
|
||||||
|
var stats = exists && fs.statSync(src);
|
||||||
|
var isDirectory = exists && stats.isDirectory();
|
||||||
|
if (exists && isDirectory) {
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(dest);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
fs.readdirSync(src).forEach(function (childItemName) {
|
||||||
|
Util.copyFolder(path.join(src, childItemName), path.join(dest, childItemName));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
fs.linkSync(src, dest);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Util.copyVolumes = function (directory, appName) {
|
||||||
|
var KITE_VOLUMES_PATH = path.join(directory, 'volumes');
|
||||||
|
if (fs.existsSync(KITE_VOLUMES_PATH)) {
|
||||||
|
var destinationPath = path.join(KITE_PATH, appName);
|
||||||
|
Util.copyFolder(KITE_VOLUMES_PATH, destinationPath);
|
||||||
|
console.log('Copied volumes for: ' + appName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Util.hasDockerfile = function (directory) {
|
||||||
|
return fs.existsSync(path.join(directory, 'Dockerfile'));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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:
|
||||||
|
* <ul>
|
||||||
|
* <li>
|
||||||
|
* <tt>lexicographical: true</tt> compares each part of the version strings lexicographically instead of
|
||||||
|
* naturally; this allows suffixes such as "b" or "dev" but will cause "1.10" to be considered smaller than
|
||||||
|
* "1.2".
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* <tt>zeroExtend: true</tt> changes the result if one version string has less parts than the other. In
|
||||||
|
* this case the shorter string will be padded with "zero" parts instead of being considered smaller.
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
* @returns {number|NaN}
|
||||||
|
* <ul>
|
||||||
|
* <li>0 if the versions are equal</li>
|
||||||
|
* <li>a negative integer iff v1 < v2</li>
|
||||||
|
* <li>a positive integer iff v1 > v2</li>
|
||||||
|
* <li>NaN if either version string is in the wrong format</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Util.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;
|
||||||
|
};
|
||||||
|
|
|
@ -1,10 +1,55 @@
|
||||||
removeBindFolder = function (name, callback) {
|
Apps.restart = function (app, callback) {
|
||||||
exec(path.join(getBinDir(), 'boot2docker') + ' ssh "sudo rm -rf /var/lib/docker/binds/' + name + '"', function(err, stdout) {
|
if (app.docker && app.docker.Id) {
|
||||||
callback(err, stdout);
|
try {
|
||||||
});
|
Docker.restartContainerSync(app.docker.Id);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
var containerData = Docker.getContainerDataSync(app.docker.Id);
|
||||||
|
Fiber(function () {
|
||||||
|
Apps.update(app._id, {$set: {
|
||||||
|
status: 'READY',
|
||||||
|
docker: containerData
|
||||||
|
}});
|
||||||
|
}).run();
|
||||||
|
callback(null);
|
||||||
|
// Use dig to refresh the DNS
|
||||||
|
exec('/usr/bin/dig dig ' + app.name + '.dev @172.17.42.1 ', function() {});
|
||||||
|
} else {
|
||||||
|
callback(null);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
recoverApps = function (callback) {
|
Apps.logs = function (app) {
|
||||||
|
if (app.docker && app.docker.Id) {
|
||||||
|
var container = docker.getContainer(app.docker.Id);
|
||||||
|
container.logs({follow: false, stdout: true, stderr: true, timestamps: true, tail: 300}, function (err, response) {
|
||||||
|
if (err) { throw err; }
|
||||||
|
Fiber(function () {
|
||||||
|
Apps.update(app._id, {
|
||||||
|
$set: {
|
||||||
|
logs: []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).run();
|
||||||
|
var logs = [];
|
||||||
|
response.setEncoding('utf8');
|
||||||
|
response.on('data', function (line) {
|
||||||
|
logs.push(convert.toHtml(line.slice(8)));
|
||||||
|
Fiber(function () {
|
||||||
|
Apps.update(app._id, {
|
||||||
|
$set: {
|
||||||
|
logs: logs
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).run();
|
||||||
|
});
|
||||||
|
response.on('end', function () {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Apps.recover = function (callback) {
|
||||||
var apps = Apps.find({}).fetch();
|
var apps = Apps.find({}).fetch();
|
||||||
_.each(apps, function (app) {
|
_.each(apps, function (app) {
|
||||||
// Update the app with the latest container info
|
// Update the app with the latest container info
|
||||||
|
@ -17,7 +62,7 @@ recoverApps = function (callback) {
|
||||||
console.log('restarting: ' + app.name);
|
console.log('restarting: ' + app.name);
|
||||||
console.log(app.docker.Id);
|
console.log(app.docker.Id);
|
||||||
Fiber(function () {
|
Fiber(function () {
|
||||||
restartApp(app, function (err) {
|
Apps.restart(app, function (err) {
|
||||||
if (err) { console.error(err); }
|
if (err) { console.error(err); }
|
||||||
});
|
});
|
||||||
}).run();
|
}).run();
|
||||||
|
@ -29,7 +74,8 @@ recoverApps = function (callback) {
|
||||||
|
|
||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
recoverApps: function () {
|
recoverApps: function () {
|
||||||
return Meteor._wrapAsync(recoverApps)();
|
this.unblock();
|
||||||
|
return Meteor._wrapAsync(Apps.recover)();
|
||||||
},
|
},
|
||||||
configVar: function (appId, configVars) {
|
configVar: function (appId, configVars) {
|
||||||
this.unblock();
|
this.unblock();
|
||||||
|
@ -48,64 +94,38 @@ Meteor.methods({
|
||||||
if (!app) {
|
if (!app) {
|
||||||
throw new Meteor.Error(403, 'No app found with this ID');
|
throw new Meteor.Error(403, 'No app found with this ID');
|
||||||
}
|
}
|
||||||
deleteApp(app, function (err) {
|
Apps.remove({_id: app._id});
|
||||||
if (err) { console.error(err); }
|
|
||||||
var appPath = path.join(KITE_PATH, app.name);
|
|
||||||
deleteFolder(appPath);
|
|
||||||
removeBindFolder(app.name, function () {
|
|
||||||
console.log('Deleted Kite ' + app.name + ' directory.');
|
|
||||||
Fiber(function () {
|
|
||||||
Apps.remove({_id: app._id});
|
|
||||||
}).run();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
createApp: function (formData) {
|
createApp: function (formData) {
|
||||||
|
this.unblock();
|
||||||
var validationResult = formValidate(formData, FormSchema.formCreateApp);
|
var validationResult = formValidate(formData, FormSchema.formCreateApp);
|
||||||
if (validationResult.errors) {
|
if (validationResult.errors) {
|
||||||
throw new Meteor.Error(400, 'Validation Failed.', validationResult.errors);
|
throw new Meteor.Error(400, 'Validation Failed.', validationResult.errors);
|
||||||
} else {
|
} else {
|
||||||
var cleaned = validationResult.cleaned;
|
var cleaned = validationResult.cleaned;
|
||||||
var appObj = {
|
var appName = cleaned.name;
|
||||||
name: cleaned.name,
|
var appPath = path.join(KITE_PATH, appName);
|
||||||
imageId: cleaned.imageId,
|
|
||||||
status: 'STARTING',
|
|
||||||
config: {}
|
|
||||||
};
|
|
||||||
var appId = Apps.insert(appObj);
|
|
||||||
var appPath = path.join(KITE_PATH, appObj.name);
|
|
||||||
if (!fs.existsSync(appPath)) {
|
if (!fs.existsSync(appPath)) {
|
||||||
console.log('Created Kite ' + appObj.name + ' directory.');
|
console.log('Created Kite ' + appName + ' directory.');
|
||||||
fs.mkdirSync(appPath, function (err) {
|
fs.mkdirSync(appPath, function (err) {
|
||||||
if (err) { throw err; }
|
if (err) { throw err; }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Apps.update(appId, {
|
var appObj = {
|
||||||
$set: {
|
name: appName,
|
||||||
'config.APP_ID': appId,
|
imageId: cleaned.imageId,
|
||||||
path: appPath
|
status: 'STARTING',
|
||||||
}
|
config: {},
|
||||||
});
|
path: appPath
|
||||||
var image = Images.findOne(appObj.imageId);
|
};
|
||||||
loadKiteVolumes(image.path, appObj.name);
|
Apps.insert(appObj);
|
||||||
var app = Apps.findOne(appId);
|
|
||||||
removeBindFolder(app.name, function (err) {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
Fiber(function () {
|
|
||||||
Meteor.call('runApp', app, function (err) {
|
|
||||||
if (err) { throw err; }
|
|
||||||
});
|
|
||||||
}).run();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getAppLogs: function (appId) {
|
getAppLogs: function (appId) {
|
||||||
this.unblock();
|
this.unblock();
|
||||||
var app = Apps.findOne(appId);
|
var app = Apps.findOne(appId);
|
||||||
if (app) {
|
if (app) {
|
||||||
getAppLogs(app, function (err) {
|
Apps.logs(app, function (err) {
|
||||||
if (err) { throw err; }
|
if (err) { throw err; }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -117,12 +137,13 @@ Meteor.methods({
|
||||||
Apps.update(app._id, {$set: {
|
Apps.update(app._id, {$set: {
|
||||||
status: 'STARTING'
|
status: 'STARTING'
|
||||||
}});
|
}});
|
||||||
restartApp(app, function (err) {
|
Apps.restart(app, function (err) {
|
||||||
if (err) { console.error(err); }
|
if (err) { console.error(err); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resolveWatchers: function () {
|
resolveWatchers: function () {
|
||||||
|
this.unblock();
|
||||||
return Meteor._wrapAsync(resolveWatchers)();
|
return Meteor._wrapAsync(resolveWatchers)();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
KITE_PATH = path.join(Util.getHomePath(), 'Kitematic');
|
||||||
|
KITE_TAR_PATH = path.join(KITE_PATH, '.tar');
|
||||||
|
KITE_IMAGES_PATH = path.join(KITE_PATH, '.images');
|
||||||
|
|
||||||
|
if (!fs.existsSync(KITE_PATH)) {
|
||||||
|
console.log('Created Kitematic directory.');
|
||||||
|
fs.mkdirSync(KITE_PATH, function (err) {
|
||||||
|
if (err) { throw err; }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(KITE_TAR_PATH)) {
|
||||||
|
console.log('Created Kitematic .tar directory.');
|
||||||
|
fs.mkdirSync(KITE_TAR_PATH, function (err) {
|
||||||
|
if (err) { throw err; }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(KITE_IMAGES_PATH)) {
|
||||||
|
console.log('Created Kitematic .images directory.');
|
||||||
|
fs.mkdirSync(KITE_IMAGES_PATH, function (err) {
|
||||||
|
if (err) { throw err; }
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,16 +1,11 @@
|
||||||
Docker = Meteor.require('dockerode');
|
Dockerode = Meteor.require('dockerode');
|
||||||
|
|
||||||
var Convert = Meteor.require('ansi-to-html');
|
var DOCKER_HOST='192.168.60.103';
|
||||||
var convert = new Convert();
|
docker = new Dockerode({host: DOCKER_HOST, port: '2375'});
|
||||||
|
|
||||||
var DOCKER_HOST='192.168.59.103';
|
Docker = {};
|
||||||
docker = new Docker({host: '192.168.59.103', port: '2375'});
|
|
||||||
|
|
||||||
hasDockerfile = function (directory) {
|
Docker.removeContainer = function (containerId, callback) {
|
||||||
return fs.existsSync(path.join(directory, 'Dockerfile'));
|
|
||||||
};
|
|
||||||
|
|
||||||
removeContainer = function (containerId, callback) {
|
|
||||||
var container = docker.getContainer(containerId);
|
var container = docker.getContainer(containerId);
|
||||||
container.kill(function (err) {
|
container.kill(function (err) {
|
||||||
if (err) { callback(err); return; }
|
if (err) { callback(err); return; }
|
||||||
|
@ -22,28 +17,11 @@ removeContainer = function (containerId, callback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
removeContainerSync = function (containerId) {
|
Docker.removeContainerSync = function (containerId) {
|
||||||
return Meteor._wrapAsync(removeContainer)(containerId);
|
return Meteor._wrapAsync(Docker.removeContainer)(containerId);
|
||||||
};
|
};
|
||||||
|
|
||||||
deleteApp = function (app, callback) {
|
Docker.getContainerData = function (containerId, callback) {
|
||||||
if (!app.docker) {
|
|
||||||
callback(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
removeContainerSync(app.docker.Id);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
callback(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteAppSync = function (app) {
|
|
||||||
return Meteor._wrapAsync(deleteApp)(app);
|
|
||||||
};
|
|
||||||
|
|
||||||
getContainerData = function (containerId, callback) {
|
|
||||||
var container = docker.getContainer(containerId);
|
var container = docker.getContainer(containerId);
|
||||||
container.inspect(function (err, data) {
|
container.inspect(function (err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -59,11 +37,11 @@ getContainerData = function (containerId, callback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getContainerDataSync = function (containerId) {
|
Docker.getContainerDataSync = function (containerId) {
|
||||||
return Meteor._wrapAsync(getContainerData)(containerId);
|
return Meteor._wrapAsync(Docker.getContainerData)(containerId);
|
||||||
};
|
};
|
||||||
|
|
||||||
runContainer = function (app, image, callback) {
|
Docker.runContainer = function (app, image, callback) {
|
||||||
var envParam = [];
|
var envParam = [];
|
||||||
_.each(_.keys(app.config), function (key) {
|
_.each(_.keys(app.config), function (key) {
|
||||||
var builtStr = key + '=' + app.config[key];
|
var builtStr = key + '=' + app.config[key];
|
||||||
|
@ -100,11 +78,11 @@ runContainer = function (app, image, callback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
runContainerSync = function (app, image) {
|
Docker.runContainerSync = function (app, image) {
|
||||||
return Meteor._wrapAsync(runContainer)(app, image);
|
return Meteor._wrapAsync(Docker.runContainer)(app, image);
|
||||||
};
|
};
|
||||||
|
|
||||||
restartContainer = function (containerId, callback) {
|
Docker.restartContainer = function (containerId, callback) {
|
||||||
var container = docker.getContainer(containerId);
|
var container = docker.getContainer(containerId);
|
||||||
container.restart(function (err) {
|
container.restart(function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -117,83 +95,8 @@ restartContainer = function (containerId, callback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
restartContainerSync = function (containerId) {
|
Docker.restartContainerSync = function (containerId) {
|
||||||
return Meteor._wrapAsync(restartContainer)(containerId);
|
return Meteor._wrapAsync(Docker.restartContainer)(containerId);
|
||||||
};
|
|
||||||
|
|
||||||
var getFromImage = function (dockerfile) {
|
|
||||||
var patternString = "(FROM)(.*)";
|
|
||||||
var regex = new RegExp(patternString, "g");
|
|
||||||
var fromInstruction = dockerfile.match(regex);
|
|
||||||
if (fromInstruction && fromInstruction.length > 0) {
|
|
||||||
return fromInstruction[0].replace('FROM', '').trim();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
restartApp = function (app, callback) {
|
|
||||||
if (app.docker && app.docker.Id) {
|
|
||||||
try {
|
|
||||||
restartContainerSync(app.docker.Id);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
var containerData = getContainerDataSync(app.docker.Id);
|
|
||||||
Fiber(function () {
|
|
||||||
Apps.update(app._id, {$set: {
|
|
||||||
status: 'READY',
|
|
||||||
docker: containerData
|
|
||||||
}});
|
|
||||||
}).run();
|
|
||||||
callback(null);
|
|
||||||
// Use dig to refresh the DNS
|
|
||||||
exec('/usr/bin/dig dig ' + app.name + '.dev @172.17.42.1 ', function() {});
|
|
||||||
} else {
|
|
||||||
callback(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
getAppLogs = function (app) {
|
|
||||||
if (app.docker && app.docker.Id) {
|
|
||||||
var container = docker.getContainer(app.docker.Id);
|
|
||||||
container.logs({follow: false, stdout: true, stderr: true, timestamps: true, tail: 300}, function (err, response) {
|
|
||||||
if (err) { throw err; }
|
|
||||||
Fiber(function () {
|
|
||||||
Apps.update(app._id, {
|
|
||||||
$set: {
|
|
||||||
logs: []
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).run();
|
|
||||||
var logs = [];
|
|
||||||
response.setEncoding('utf8');
|
|
||||||
response.on('data', function (line) {
|
|
||||||
logs.push(convert.toHtml(line.slice(8)));
|
|
||||||
Fiber(function () {
|
|
||||||
Apps.update(app._id, {
|
|
||||||
$set: {
|
|
||||||
logs: logs
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).run();
|
|
||||||
});
|
|
||||||
response.on('end', function () {});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
createTarFile = function (image, callback) {
|
|
||||||
var TAR_PATH = path.join(KITE_TAR_PATH, image._id + '.tar');
|
|
||||||
exec('tar czf ' + TAR_PATH + ' -C ' + image.path + ' .', function (err) {
|
|
||||||
if (err) { callback(err, null); return; }
|
|
||||||
console.log('Created tar file: ' + TAR_PATH);
|
|
||||||
callback(null, TAR_PATH);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
createTarFileSync = function (image) {
|
|
||||||
return Meteor._wrapAsync(createTarFile)(image);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var convertVolumeObjToArray = function (obj) {
|
var convertVolumeObjToArray = function (obj) {
|
||||||
|
@ -209,7 +112,7 @@ var convertVolumeObjToArray = function (obj) {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
getImageData = function (imageId, callback) {
|
Docker.getImageData = function (imageId, callback) {
|
||||||
var image = docker.getImage(imageId.toLowerCase());
|
var image = docker.getImage(imageId.toLowerCase());
|
||||||
image.inspect(function (err, data) {
|
image.inspect(function (err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -224,11 +127,11 @@ getImageData = function (imageId, callback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getImageDataSync = function (imageId) {
|
Docker.getImageDataSync = function (imageId) {
|
||||||
return Meteor._wrapAsync(getImageData)(imageId);
|
return Meteor._wrapAsync(Docker.getImageData)(imageId);
|
||||||
};
|
};
|
||||||
|
|
||||||
removeImage = function (imageId, callback) {
|
Docker.removeImage = function (imageId, callback) {
|
||||||
var image = docker.getImage(imageId.toLowerCase());
|
var image = docker.getImage(imageId.toLowerCase());
|
||||||
image.remove({force: true}, function (err) {
|
image.remove({force: true}, function (err) {
|
||||||
if (err) { callback(err); return; }
|
if (err) { callback(err); return; }
|
||||||
|
@ -237,25 +140,14 @@ removeImage = function (imageId, callback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
removeImageSync = function (imageId) {
|
Docker.removeImageSync = function (imageId) {
|
||||||
return Meteor._wrapAsync(removeImage)(imageId);
|
return Meteor._wrapAsync(Docker.removeImage)(imageId);
|
||||||
};
|
};
|
||||||
|
|
||||||
deleteImage = function (image, callback) {
|
Docker.removeBindFolder = function (name, callback) {
|
||||||
if (!image.docker) {
|
exec(path.join(Util.getBinDir(), 'boot2docker') + ' ssh "sudo rm -rf /var/lib/docker/binds/' + name + '"', function (err, stdout) {
|
||||||
callback(null, {});
|
callback(err, stdout);
|
||||||
return;
|
});
|
||||||
}
|
|
||||||
try {
|
|
||||||
removeImageSync(image.docker.Id);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
callback(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteImageSync = function (image) {
|
|
||||||
return Meteor._wrapAsync(deleteImage)(image);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var defaultContainerOptions = function () {
|
var defaultContainerOptions = function () {
|
||||||
|
@ -304,7 +196,7 @@ resolveDefaultImages = function () {
|
||||||
image.inspect(function (err) {
|
image.inspect(function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.reason === 'no such image') {
|
if (err.reason === 'no such image') {
|
||||||
docker.loadImage(path.join(getBinDir(), 'base-images.tar.gz'), {}, function (err) {
|
docker.loadImage(path.join(Util.getBinDir(), 'base-images.tar.gz'), {}, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
innerCallback(err);
|
innerCallback(err);
|
||||||
return;
|
return;
|
||||||
|
@ -374,7 +266,7 @@ reloadDefaultContainers = function (callback) {
|
||||||
async.until(function () {
|
async.until(function () {
|
||||||
return ready;
|
return ready;
|
||||||
}, function (callback) {
|
}, function (callback) {
|
||||||
docker.listContainers(function (err, containers) {
|
docker.listContainers(function (err) {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
ready = true;
|
ready = true;
|
||||||
}
|
}
|
||||||
|
@ -391,7 +283,7 @@ reloadDefaultContainers = function (callback) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('Loading new Kitematic default images.');
|
console.log('Loading new Kitematic default images.');
|
||||||
docker.loadImage(path.join(getBinDir(), 'base-images.tar.gz'), {}, function (err) {
|
docker.loadImage(path.join(Util.getBinDir(), 'base-images.tar.gz'), {}, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
return;
|
return;
|
||||||
|
@ -500,142 +392,18 @@ killAndRemoveContainers = function (names, callback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
pullImageFromDockerfile = function (dockerfile, imageId, callback) {
|
|
||||||
var fromImage = getFromImage(dockerfile);
|
|
||||||
console.log('From image: ' + fromImage);
|
|
||||||
var installedImage = null;
|
|
||||||
try {
|
|
||||||
installedImage = getImageDataSync(fromImage);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
if (fromImage && !installedImage) {
|
|
||||||
Fiber(function () {
|
|
||||||
Images.update(imageId, {
|
|
||||||
$set: {
|
|
||||||
buildLogs: []
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).run();
|
|
||||||
var logs = [];
|
|
||||||
docker.pull(fromImage, function (err, response) {
|
|
||||||
if (err) { callback(err); return; }
|
|
||||||
response.setEncoding('utf8');
|
|
||||||
response.on('data', function (data) {
|
|
||||||
try {
|
|
||||||
var logData = JSON.parse(data);
|
|
||||||
var logDisplay = '';
|
|
||||||
if (logData.id) {
|
|
||||||
logDisplay += logData.id + ' | ';
|
|
||||||
}
|
|
||||||
logDisplay += logData.status;
|
|
||||||
if (logData.progressDetail && logData.progressDetail.current && logData.progressDetail.total) {
|
|
||||||
logDisplay += ' - ' + Math.round(logData.progressDetail.current / logData.progressDetail.total * 100) + '%';
|
|
||||||
}
|
|
||||||
logs.push(logDisplay);
|
|
||||||
Fiber(function () {
|
|
||||||
Images.update(imageId, {
|
|
||||||
$set: {
|
|
||||||
buildLogs: logs
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).run();
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
response.on('end', function () {
|
|
||||||
console.log('Finished pulling image: ' + fromImage);
|
|
||||||
callback(null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
callback(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
buildImage = function (image, callback) {
|
|
||||||
Fiber(function () {
|
|
||||||
var tarFilePath = createTarFileSync(image);
|
|
||||||
Images.update(image._id, {
|
|
||||||
$set: {
|
|
||||||
buildLogs: []
|
|
||||||
}
|
|
||||||
});
|
|
||||||
docker.buildImage(tarFilePath, {t: image._id.toLowerCase()}, function (err, response) {
|
|
||||||
if (err) { callback(err); }
|
|
||||||
console.log('Building Docker image...');
|
|
||||||
var logs = [];
|
|
||||||
response.setEncoding('utf8');
|
|
||||||
response.on('data', function (data) {
|
|
||||||
try {
|
|
||||||
var line = JSON.parse(data).stream;
|
|
||||||
logs.push(convert.toHtml(line));
|
|
||||||
Fiber(function () {
|
|
||||||
Images.update(image._id, {
|
|
||||||
$set: {
|
|
||||||
buildLogs: logs
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).run();
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
response.on('end', function () {
|
|
||||||
console.log('Finished building Docker image.');
|
|
||||||
try {
|
|
||||||
fs.unlinkSync(tarFilePath);
|
|
||||||
console.log('Cleaned up tar file.');
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
Fiber(function () {
|
|
||||||
var imageData = null;
|
|
||||||
try {
|
|
||||||
imageData = getImageDataSync(image._id);
|
|
||||||
Images.update(image._id, {
|
|
||||||
$set: {
|
|
||||||
docker: imageData,
|
|
||||||
status: 'READY'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
Images.update(image._id, {
|
|
||||||
$set: {
|
|
||||||
status: 'ERROR'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
var oldImageId = null;
|
|
||||||
if (image.docker && image.docker.Id) {
|
|
||||||
oldImageId = image.docker.Id;
|
|
||||||
}
|
|
||||||
if (oldImageId && imageData && oldImageId !== imageData.Id) {
|
|
||||||
try {
|
|
||||||
removeImageSync(oldImageId);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).run();
|
|
||||||
callback(null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}).run();
|
|
||||||
};
|
|
||||||
|
|
||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
runApp: function (app) {
|
runApp: function (app) {
|
||||||
this.unblock();
|
this.unblock();
|
||||||
var image = Images.findOne({_id: app.imageId});
|
var image = Images.findOne({_id: app.imageId});
|
||||||
|
// Delete old container if one already exists
|
||||||
try {
|
try {
|
||||||
removeContainerSync(app.name);
|
Docker.removeContainerSync(app.name);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
try {
|
try {
|
||||||
var container = runContainerSync(app, image);
|
var container = Docker.runContainerSync(app, image);
|
||||||
var containerData = getContainerDataSync(container.id);
|
var containerData = Docker.getContainerDataSync(container.id);
|
||||||
|
// Set a delay for app to spin up
|
||||||
Meteor.setTimeout(function () {
|
Meteor.setTimeout(function () {
|
||||||
Apps.update(app._id, {$set: {
|
Apps.update(app._id, {$set: {
|
||||||
docker: containerData,
|
docker: containerData,
|
||||||
|
@ -650,18 +418,23 @@ Meteor.methods({
|
||||||
return DOCKER_HOST;
|
return DOCKER_HOST;
|
||||||
},
|
},
|
||||||
reloadDefaultContainers: function () {
|
reloadDefaultContainers: function () {
|
||||||
|
this.unblock();
|
||||||
return Meteor._wrapAsync(reloadDefaultContainers)();
|
return Meteor._wrapAsync(reloadDefaultContainers)();
|
||||||
},
|
},
|
||||||
checkDefaultImages: function () {
|
checkDefaultImages: function () {
|
||||||
|
this.unblock();
|
||||||
return Meteor._wrapAsync(checkDefaultImages)();
|
return Meteor._wrapAsync(checkDefaultImages)();
|
||||||
},
|
},
|
||||||
resolveDefaultImages: function () {
|
resolveDefaultImages: function () {
|
||||||
|
this.unblock();
|
||||||
return Meteor._wrapAsync(resolveDefaultImages)();
|
return Meteor._wrapAsync(resolveDefaultImages)();
|
||||||
},
|
},
|
||||||
checkDefaultContainers: function () {
|
checkDefaultContainers: function () {
|
||||||
|
this.unblock();
|
||||||
return Meteor._wrapAsync(checkDefaultContainers)();
|
return Meteor._wrapAsync(checkDefaultContainers)();
|
||||||
},
|
},
|
||||||
resolveDefaultContainers: function () {
|
resolveDefaultContainers: function () {
|
||||||
|
this.unblock();
|
||||||
return Meteor._wrapAsync(resolveDefaultContainers)();
|
return Meteor._wrapAsync(resolveDefaultContainers)();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,38 @@
|
||||||
getImageMetaData = function (directory) {
|
var createTarFile = function (image, callback) {
|
||||||
|
var TAR_PATH = path.join(KITE_TAR_PATH, image._id + '.tar');
|
||||||
|
exec('tar czf ' + TAR_PATH + ' -C ' + image.path + ' .', function (err) {
|
||||||
|
if (err) { callback(err, null); return; }
|
||||||
|
console.log('Created tar file: ' + TAR_PATH);
|
||||||
|
callback(null, TAR_PATH);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var createTarFileSync = function (image) {
|
||||||
|
return Meteor._wrapAsync(createTarFile)(image);
|
||||||
|
};
|
||||||
|
|
||||||
|
var getFromImage = function (dockerfile) {
|
||||||
|
var patternString = "(FROM)(.*)";
|
||||||
|
var regex = new RegExp(patternString, "g");
|
||||||
|
var fromInstruction = dockerfile.match(regex);
|
||||||
|
if (fromInstruction && fromInstruction.length > 0) {
|
||||||
|
return fromInstruction[0].replace('FROM', '').trim();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var getImageJSON = function (directory) {
|
||||||
|
var KITE_JSON_PATH = path.join(directory, 'image.json');
|
||||||
|
if (fs.existsSync(KITE_JSON_PATH)) {
|
||||||
|
var data = fs.readFileSync(KITE_JSON_PATH, 'utf8');
|
||||||
|
return JSON.parse(data);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var getImageMetaData = function (directory) {
|
||||||
var kiteJSON = getImageJSON(directory);
|
var kiteJSON = getImageJSON(directory);
|
||||||
if (kiteJSON) {
|
if (kiteJSON) {
|
||||||
if (!kiteJSON.name) {
|
if (!kiteJSON.name) {
|
||||||
|
@ -12,8 +46,24 @@ getImageMetaData = function (directory) {
|
||||||
return kiteJSON;
|
return kiteJSON;
|
||||||
};
|
};
|
||||||
|
|
||||||
rebuildImage = function (image, callback) {
|
Images.saveFolder = function (directory, imageId, callback) {
|
||||||
deleteFolder(image.path);
|
var destinationPath = path.join(KITE_IMAGES_PATH, imageId);
|
||||||
|
if (!fs.existsSync(destinationPath)) {
|
||||||
|
fs.mkdirSync(destinationPath, function (err) {
|
||||||
|
if (err) { callback(err); return; }
|
||||||
|
});
|
||||||
|
Util.copyFolder(directory, destinationPath);
|
||||||
|
console.log('Copied image folder for: ' + imageId);
|
||||||
|
callback(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Images.saveFolderSync = function (directory, imageId) {
|
||||||
|
return Meteor._wrapAsync(Images.saveFolder)(directory, imageId);
|
||||||
|
};
|
||||||
|
|
||||||
|
Images.rebuild = function (image, callback) {
|
||||||
|
Util.deleteFolder(image.path);
|
||||||
var imageMetaData = getImageMetaData(image.originPath);
|
var imageMetaData = getImageMetaData(image.originPath);
|
||||||
if (imageMetaData.logo) {
|
if (imageMetaData.logo) {
|
||||||
Images.update(image._id, {
|
Images.update(image._id, {
|
||||||
|
@ -35,18 +85,144 @@ rebuildImage = function (image, callback) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
image = Images.findOne(image._id);
|
image = Images.findOne(image._id);
|
||||||
saveImageFolderSync(image.originPath, image._id);
|
Images.saveFolderSync(image.originPath, image._id);
|
||||||
pullImageFromDockerfile(fs.readFileSync(path.join(image.path, 'Dockerfile'), 'utf8'), image._id, function (err) {
|
Images.pull(fs.readFileSync(path.join(image.path, 'Dockerfile'), 'utf8'), image._id, function (err) {
|
||||||
if (err) { callback(err, null); return; }
|
if (err) { callback(err, null); return; }
|
||||||
buildImage(image, function (err) {
|
Images.build(image, function (err) {
|
||||||
if (err) { console.error(err); }
|
if (err) { console.error(err); }
|
||||||
callback(null, null);
|
callback(null, null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
rebuildImageSync = function (image) {
|
Images.rebuildSync = function (image) {
|
||||||
return Meteor._wrapAsync(rebuildImage)(image);
|
return Meteor._wrapAsync(Images.rebuild)(image);
|
||||||
|
};
|
||||||
|
|
||||||
|
Images.pull = function (dockerfile, imageId, callback) {
|
||||||
|
var fromImage = getFromImage(dockerfile);
|
||||||
|
console.log('From image: ' + fromImage);
|
||||||
|
var installedImage = null;
|
||||||
|
try {
|
||||||
|
installedImage = Docker.getImageDataSync(fromImage);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
if (fromImage && !installedImage) {
|
||||||
|
Fiber(function () {
|
||||||
|
Images.update(imageId, {
|
||||||
|
$set: {
|
||||||
|
buildLogs: []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).run();
|
||||||
|
var logs = [];
|
||||||
|
docker.pull(fromImage, function (err, response) {
|
||||||
|
if (err) { callback(err); return; }
|
||||||
|
response.setEncoding('utf8');
|
||||||
|
response.on('data', function (data) {
|
||||||
|
try {
|
||||||
|
var logData = JSON.parse(data);
|
||||||
|
var logDisplay = '';
|
||||||
|
if (logData.id) {
|
||||||
|
logDisplay += logData.id + ' | ';
|
||||||
|
}
|
||||||
|
logDisplay += logData.status;
|
||||||
|
if (logData.progressDetail && logData.progressDetail.current && logData.progressDetail.total) {
|
||||||
|
logDisplay += ' - ' + Math.round(logData.progressDetail.current / logData.progressDetail.total * 100) + '%';
|
||||||
|
}
|
||||||
|
logs.push(logDisplay);
|
||||||
|
Fiber(function () {
|
||||||
|
Images.update(imageId, {
|
||||||
|
$set: {
|
||||||
|
buildLogs: logs
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).run();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
response.on('end', function () {
|
||||||
|
console.log('Finished pulling image: ' + fromImage);
|
||||||
|
callback(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
callback(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Images.build = function (image, callback) {
|
||||||
|
Fiber(function () {
|
||||||
|
var tarFilePath = createTarFileSync(image);
|
||||||
|
Images.update(image._id, {
|
||||||
|
$set: {
|
||||||
|
buildLogs: []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
docker.buildImage(tarFilePath, {t: image._id.toLowerCase()}, function (err, response) {
|
||||||
|
if (err) { callback(err); }
|
||||||
|
console.log('Building Docker image...');
|
||||||
|
var logs = [];
|
||||||
|
response.setEncoding('utf8');
|
||||||
|
response.on('data', function (data) {
|
||||||
|
try {
|
||||||
|
var line = JSON.parse(data).stream;
|
||||||
|
logs.push(convert.toHtml(line));
|
||||||
|
Fiber(function () {
|
||||||
|
Images.update(image._id, {
|
||||||
|
$set: {
|
||||||
|
buildLogs: logs
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).run();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
response.on('end', function () {
|
||||||
|
console.log('Finished building Docker image.');
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(tarFilePath);
|
||||||
|
console.log('Cleaned up tar file.');
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
Fiber(function () {
|
||||||
|
var imageData = null;
|
||||||
|
try {
|
||||||
|
imageData = Docker.getImageDataSync(image._id);
|
||||||
|
Images.update(image._id, {
|
||||||
|
$set: {
|
||||||
|
docker: imageData,
|
||||||
|
status: 'READY'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
Images.update(image._id, {
|
||||||
|
$set: {
|
||||||
|
status: 'ERROR'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var oldImageId = null;
|
||||||
|
if (image.docker && image.docker.Id) {
|
||||||
|
oldImageId = image.docker.Id;
|
||||||
|
}
|
||||||
|
if (oldImageId && imageData && oldImageId !== imageData.Id) {
|
||||||
|
try {
|
||||||
|
Docker.removeImageSync(oldImageId);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).run();
|
||||||
|
callback(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).run();
|
||||||
};
|
};
|
||||||
|
|
||||||
Meteor.methods({
|
Meteor.methods({
|
||||||
|
@ -58,29 +234,7 @@ Meteor.methods({
|
||||||
};
|
};
|
||||||
var imageMetaData = getImageMetaData(directory);
|
var imageMetaData = getImageMetaData(directory);
|
||||||
imageObj.meta = imageMetaData;
|
imageObj.meta = imageMetaData;
|
||||||
var imageId = Images.insert(imageObj);
|
Images.insert(imageObj);
|
||||||
var imagePath = path.join(KITE_IMAGES_PATH, imageId);
|
|
||||||
Images.update(imageId, {
|
|
||||||
$set: {
|
|
||||||
path: imagePath
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (imageObj.meta.logo) {
|
|
||||||
Images.update(imageId, {
|
|
||||||
$set: {
|
|
||||||
logoPath: path.join(imagePath, imageObj.meta.logo)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
var image = Images.findOne(imageId);
|
|
||||||
saveImageFolderSync(directory, imageId);
|
|
||||||
console.log('Saved folder sync');
|
|
||||||
pullImageFromDockerfile(fs.readFileSync(path.join(image.path, 'Dockerfile'), 'utf8'), imageId, function (err) {
|
|
||||||
if (err) { throw err; }
|
|
||||||
buildImage(image, function (err) {
|
|
||||||
if (err) { console.error(err); }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
rebuildImage: function (imageId) {
|
rebuildImage: function (imageId) {
|
||||||
this.unblock();
|
this.unblock();
|
||||||
|
@ -92,7 +246,13 @@ Meteor.methods({
|
||||||
if (apps.length > 0) {
|
if (apps.length > 0) {
|
||||||
_.each(apps, function (app) {
|
_.each(apps, function (app) {
|
||||||
console.log('Updating app: ' + app.name);
|
console.log('Updating app: ' + app.name);
|
||||||
deleteAppSync(app);
|
if (app.docker) {
|
||||||
|
try {
|
||||||
|
Docker.removeContainerSync(app.docker.Id);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
Apps.update(app._id, {
|
Apps.update(app._id, {
|
||||||
$set: {
|
$set: {
|
||||||
'docker.Id': null,
|
'docker.Id': null,
|
||||||
|
@ -101,7 +261,7 @@ Meteor.methods({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
rebuildImageSync(image);
|
Images.rebuildSync(image);
|
||||||
_.each(apps, function (app) {
|
_.each(apps, function (app) {
|
||||||
app = Apps.findOne(app._id);
|
app = Apps.findOne(app._id);
|
||||||
Meteor.call('runApp', app, function (err) {
|
Meteor.call('runApp', app, function (err) {
|
||||||
|
@ -109,7 +269,7 @@ Meteor.methods({
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
rebuildImageSync(image);
|
Images.rebuildSync(image);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
changeDirectory: function (imageId, directory) {
|
changeDirectory: function (imageId, directory) {
|
||||||
|
@ -125,7 +285,8 @@ Meteor.methods({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
validateDirectory: function (directory) {
|
validateDirectory: function (directory) {
|
||||||
if (!hasDockerfile(directory)) {
|
this.unblock();
|
||||||
|
if (!Util.hasDockerfile(directory)) {
|
||||||
throw new Meteor.Error(400, "Only directories with Dockerfiles are supported now.");
|
throw new Meteor.Error(400, "Only directories with Dockerfiles are supported now.");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -137,15 +298,7 @@ Meteor.methods({
|
||||||
}
|
}
|
||||||
var app = Apps.findOne({imageId: imageId});
|
var app = Apps.findOne({imageId: imageId});
|
||||||
if (!app) {
|
if (!app) {
|
||||||
console.log('here');
|
Images.remove({_id: image._id});
|
||||||
try {
|
|
||||||
deleteImageSync(image);
|
|
||||||
deleteFolder(image.path);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
} finally {
|
|
||||||
Images.remove({_id: image._id});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw new Meteor.Error(400, 'This image is currently being used by <a href="/apps/' + app.name + '">' + app.name + "</a>.");
|
throw new Meteor.Error(400, 'This image is currently being used by <a href="/apps/' + app.name + '">' + app.name + "</a>.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
KITE_PATH = path.join(getHomePath(), 'Kitematic');
|
|
||||||
KITE_TAR_PATH = path.join(KITE_PATH, '.tar');
|
|
||||||
KITE_IMAGES_PATH = path.join(KITE_PATH, '.images');
|
|
||||||
|
|
||||||
if (!fs.existsSync(KITE_PATH)) {
|
|
||||||
console.log('Created Kitematic directory.');
|
|
||||||
fs.mkdirSync(KITE_PATH, function (err) {
|
|
||||||
if (err) { throw err; }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fs.existsSync(KITE_TAR_PATH)) {
|
|
||||||
console.log('Created Kitematic .tar directory.');
|
|
||||||
fs.mkdirSync(KITE_TAR_PATH, function (err) {
|
|
||||||
if (err) { throw err; }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fs.existsSync(KITE_IMAGES_PATH)) {
|
|
||||||
console.log('Created Kitematic .images directory.');
|
|
||||||
fs.mkdirSync(KITE_IMAGES_PATH, function (err) {
|
|
||||||
if (err) { throw err; }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getImageJSON = function (directory) {
|
|
||||||
var KITE_JSON_PATH = path.join(directory, 'image.json');
|
|
||||||
if (fs.existsSync(KITE_JSON_PATH)) {
|
|
||||||
var data = fs.readFileSync(KITE_JSON_PATH, 'utf8');
|
|
||||||
return JSON.parse(data);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
loadKiteVolumes = function (directory, appName) {
|
|
||||||
var KITE_VOLUMES_PATH = path.join(directory, 'volumes');
|
|
||||||
if (fs.existsSync(KITE_VOLUMES_PATH)) {
|
|
||||||
var destinationPath = path.join(KITE_PATH, appName);
|
|
||||||
copyFolder(KITE_VOLUMES_PATH, destinationPath);
|
|
||||||
console.log('Copied volumes for: ' + appName);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
saveImageFolder = function (directory, imageId, callback) {
|
|
||||||
var destinationPath = path.join(KITE_IMAGES_PATH, imageId);
|
|
||||||
if (!fs.existsSync(destinationPath)) {
|
|
||||||
fs.mkdirSync(destinationPath, function (err) {
|
|
||||||
if (err) { callback(err); return; }
|
|
||||||
});
|
|
||||||
copyFolder(directory, destinationPath);
|
|
||||||
console.log('Copied image folder for: ' + imageId);
|
|
||||||
callback(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
saveImageFolderSync = function (directory, imageId) {
|
|
||||||
return Meteor._wrapAsync(saveImageFolder)(directory, imageId);
|
|
||||||
};
|
|
|
@ -6,4 +6,6 @@ path = Meteor.require('path');
|
||||||
exec = Meteor.require('child_process').exec;
|
exec = Meteor.require('child_process').exec;
|
||||||
async = Meteor.require('async');
|
async = Meteor.require('async');
|
||||||
Fiber = Meteor.require('fibers');
|
Fiber = Meteor.require('fibers');
|
||||||
child_process = Meteor.require('child_process');
|
child_process = Meteor.require('child_process');
|
||||||
|
Convert = Meteor.require('ansi-to-html');
|
||||||
|
convert = new Convert();
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
getHomePath = function () {
|
|
||||||
return process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];
|
|
||||||
};
|
|
||||||
|
|
||||||
getBinDir = function () {
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
return path.join(path.join(process.env.PWD, '..'), 'resources');
|
|
||||||
} else {
|
|
||||||
return path.join(process.cwd(), '../../../resources');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteFolder = function (directory) {
|
|
||||||
if (fs.existsSync(directory)) {
|
|
||||||
fs.readdirSync(directory).forEach(function (file) {
|
|
||||||
var curDirectory = directory + '/' + file;
|
|
||||||
if (fs.lstatSync(curDirectory).isDirectory()) {
|
|
||||||
// Recurse
|
|
||||||
deleteFolder(curDirectory);
|
|
||||||
} else {
|
|
||||||
// Delete File
|
|
||||||
try {
|
|
||||||
fs.unlinkSync(curDirectory);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
fs.rmdirSync(directory);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
copyFolder = function (src, dest) {
|
|
||||||
var exists = fs.existsSync(src);
|
|
||||||
var stats = exists && fs.statSync(src);
|
|
||||||
var isDirectory = exists && stats.isDirectory();
|
|
||||||
if (exists && isDirectory) {
|
|
||||||
try {
|
|
||||||
fs.mkdirSync(dest);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
fs.readdirSync(src).forEach(function (childItemName) {
|
|
||||||
copyFolder(path.join(src, childItemName), path.join(dest, childItemName));
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
fs.linkSync(src, dest);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -7,5 +7,5 @@ Meteor.publish('images', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
Meteor.publish('installs', function () {
|
Meteor.publish('installs', function () {
|
||||||
return Installs.find({}, {sort: {createdAt: -1}});
|
return Installs.find({});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,45 +1,39 @@
|
||||||
#!/bin/sh
|
#!/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 .kite domains to the virtual box VM with the name
|
||||||
|
# 'boot2docker-vm'. It does the following:
|
||||||
|
# 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
|
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")
|
DIR=$(dirname "$0")
|
||||||
USER=`w -h | sort -u -t' ' -k1,1 | awk '{print $1}'`
|
USER=`w -h | sort -u -t' ' -k1,1 | awk '{print $1}'`
|
||||||
|
|
||||||
/bin/rm -rf /Library/LaunchAgents/com.kitematic.route.plist
|
/bin/rm -rf /Library/LaunchAgents/com.kitematic.route.plist
|
||||||
|
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
||||||
echo '<?xml version="1.0" encoding="UTF-8"?>
|
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<plist version=\"1.0\">
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
<dict>
|
||||||
<key>Label</key>
|
<key>Label</key>
|
||||||
<string>com.kitematic.route</string>
|
<string>com.kitematic.route</string>
|
||||||
<key>ProgramArguments</key>
|
<key>ProgramArguments</key>
|
||||||
<array>
|
<array>
|
||||||
<string>/sbin/route</string>
|
<string>bash</string>
|
||||||
<string>-n</string>
|
<string>-c</string>
|
||||||
<string>add</string>
|
<string>/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</string>
|
||||||
<string>172.17.0.0/16</string>
|
|
||||||
<string>192.168.59.103</string>
|
|
||||||
</array>
|
</array>
|
||||||
<key>KeepAlive</key>
|
<key>KeepAlive</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>RunAtLoad</key>
|
<key>RunAtLoad</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>ServiceIPC</key>
|
|
||||||
<false/>
|
|
||||||
<key>UserName</key>
|
|
||||||
<string>root</string>
|
|
||||||
<key>LaunchOnlyOnce</key>
|
<key>LaunchOnlyOnce</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>' > /Library/LaunchAgents/com.kitematic.route.plist
|
</plist>" > /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
|
|
||||||
|
|
||||||
# Add entries to routing table for Kitematic VM
|
# Add entries to routing table for Kitematic VM
|
||||||
/sbin/route delete 172.17.0.0/16 192.168.59.103
|
/sbin/route delete -net 172.17.0.0 -netmask 255.255.0.0 -gateway $GATEWAY
|
||||||
/sbin/route -n add 172.17.0.0/16 192.168.59.103
|
/sbin/route -n add -net 172.17.0.0 -netmask 255.255.0.0 -gateway $GATEWAY
|
|
@ -1,6 +1,6 @@
|
||||||
user=root
|
user=root
|
||||||
{{ range $index, $value := $ }}
|
{{ range $index, $value := $ }}
|
||||||
{{ with $address := index $value.Addresses 0 }}
|
{{ with $address := index $value.Addresses 0 }}
|
||||||
address=/{{$value.Name}}.dev/{{$address.IP}}
|
address=/{{$value.Name}}.kite/{{$address.IP}}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -15,6 +15,7 @@ pushd cache
|
||||||
if [ ! -f $BASE_IMAGE_VERSION_FILE ]; then
|
if [ ! -f $BASE_IMAGE_VERSION_FILE ]; then
|
||||||
cecho "-----> Downloading Kitematic base images..." $purple
|
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
|
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
|
fi
|
||||||
|
|
||||||
if [ ! -f $BOOT2DOCKER_CLI_VERSION_FILE ]; then
|
if [ ! -f $BOOT2DOCKER_CLI_VERSION_FILE ]; then
|
||||||
|
@ -60,11 +61,6 @@ if [ ! -f $COCOASUDO_FILE ]; then
|
||||||
chmod +x $COCOASUDO_FILE
|
chmod +x $COCOASUDO_FILE
|
||||||
fi
|
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
|
cp ../cache/$BOOT2DOCKER_CLI_VERSION_FILE $BOOT2DOCKER_CLI_FILE
|
||||||
chmod +x $BOOT2DOCKER_CLI_FILE
|
chmod +x $BOOT2DOCKER_CLI_FILE
|
||||||
|
|
||||||
|
|
|
@ -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_VERSION_FILE=base-images-$BASE_IMAGE_VERSION.tar.gz
|
||||||
BASE_IMAGE_FILE=base-images.tar.gz
|
BASE_IMAGE_FILE=base-images.tar.gz
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue