Bumping virtualbox

This commit is contained in:
Jeff Morgan 2014-09-03 21:02:31 -07:00
parent ec697f8075
commit f263f76b4b
5 changed files with 321 additions and 113 deletions

View File

@ -4,9 +4,9 @@ var path = require('path');
VirtualBox = {}; VirtualBox = {};
VirtualBox.REQUIRED_VERSION = '4.3.12'; VirtualBox.REQUIRED_VERSION = '4.3.14';
VirtualBox.INCLUDED_VERSION = '4.3.12'; VirtualBox.INCLUDED_VERSION = '4.3.14';
VirtualBox.INSTALLER_FILENAME = 'virtualbox-4.3.12.pkg'; VirtualBox.INSTALLER_FILENAME = 'virtualbox-4.3.14.pkg';
// Info for the hostonly interface we add to the VM. // Info for the hostonly interface we add to the VM.
VirtualBox.HOSTONLY_HOSTIP = '192.168.60.3'; VirtualBox.HOSTONLY_HOSTIP = '192.168.60.3';
@ -25,6 +25,8 @@ VirtualBox.exec = function (command, callback) {
VirtualBox.install = function (callback) { VirtualBox.install = function (callback) {
// -W waits for the process to close before finishing. // -W waits for the process to close before finishing.
exec('open -W ' + path.join(Util.getBinDir(), this.INSTALLER_FILENAME), function (error, stdout, stderr) { exec('open -W ' + path.join(Util.getBinDir(), this.INSTALLER_FILENAME), function (error, stdout, stderr) {
console.log(stdout);
console.log(stderr);
if (error) { if (error) {
callback(error); callback(error);
return; return;

View File

@ -1,55 +1,10 @@
Apps.restart = function (app, callback) { removeBindFolder = function (name, callback) {
if (app.docker && app.docker.Id) { exec(path.join(getBinDir(), 'boot2docker') + ' ssh "sudo rm -rf /var/lib/docker/binds/' + name + '"', function(err, stdout) {
try { callback(err, stdout);
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 + '.kite @172.17.42.1 ', function() {});
} else {
callback(null);
}
}; };
Apps.logs = function (app) { recoverApps = function (callback) {
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
@ -62,7 +17,7 @@ Apps.recover = 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 () {
Apps.restart(app, function (err) { restartApp(app, function (err) {
if (err) { console.error(err); } if (err) { console.error(err); }
}); });
}).run(); }).run();
@ -74,8 +29,7 @@ Apps.recover = function (callback) {
Meteor.methods({ Meteor.methods({
recoverApps: function () { recoverApps: function () {
this.unblock(); return Meteor._wrapAsync(recoverApps)();
return Meteor._wrapAsync(Apps.recover)();
}, },
configVar: function (appId, configVars) { configVar: function (appId, configVars) {
this.unblock(); this.unblock();
@ -94,38 +48,64 @@ 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');
} }
Apps.remove({_id: app._id}); deleteApp(app, function (err) {
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 appName = cleaned.name; var appObj = {
var appPath = path.join(KITE_PATH, appName); name: cleaned.name,
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 ' + appName + ' directory.'); console.log('Created Kite ' + appObj.name + ' directory.');
fs.mkdirSync(appPath, function (err) { fs.mkdirSync(appPath, function (err) {
if (err) { throw err; } if (err) { throw err; }
}); });
} }
var appObj = { Apps.update(appId, {
name: appName, $set: {
imageId: cleaned.imageId, 'config.APP_ID': appId,
status: 'STARTING', path: appPath
config: {}, }
path: appPath });
}; var image = Images.findOne(appObj.imageId);
Apps.insert(appObj); loadKiteVolumes(image.path, appObj.name);
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) {
Apps.logs(app, function (err) { getAppLogs(app, function (err) {
if (err) { throw err; } if (err) { throw err; }
}); });
} }
@ -137,13 +117,12 @@ Meteor.methods({
Apps.update(app._id, {$set: { Apps.update(app._id, {$set: {
status: 'STARTING' status: 'STARTING'
}}); }});
Apps.restart(app, function (err) { restartApp(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)();
} }
}); });

View File

@ -1,11 +1,16 @@
Dockerode = Meteor.require('dockerode'); Docker = Meteor.require('dockerode');
var DOCKER_HOST='192.168.60.103'; var Convert = Meteor.require('ansi-to-html');
docker = new Dockerode({host: DOCKER_HOST, port: '2375'}); var convert = new Convert();
Docker = {}; var DOCKER_HOST='192.168.59.103';
docker = new Docker({host: '192.168.59.103', port: '2375'});
Docker.removeContainer = function (containerId, callback) { hasDockerfile = function (directory) {
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; }
@ -17,11 +22,28 @@ Docker.removeContainer = function (containerId, callback) {
}); });
}; };
Docker.removeContainerSync = function (containerId) { removeContainerSync = function (containerId) {
return Meteor._wrapAsync(Docker.removeContainer)(containerId); return Meteor._wrapAsync(removeContainer)(containerId);
}; };
Docker.getContainerData = function (containerId, callback) { deleteApp = function (app, 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) {
@ -37,11 +59,11 @@ Docker.getContainerData = function (containerId, callback) {
}); });
}; };
Docker.getContainerDataSync = function (containerId) { getContainerDataSync = function (containerId) {
return Meteor._wrapAsync(Docker.getContainerData)(containerId); return Meteor._wrapAsync(getContainerData)(containerId);
}; };
Docker.runContainer = function (app, image, callback) { 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];
@ -72,17 +94,17 @@ Docker.runContainer = function (app, image, callback) {
if (err) { callback(err, null); return; } if (err) { callback(err, null); return; }
console.log('Started container: ' + container.id); console.log('Started container: ' + container.id);
// Use dig to refresh the DNS // Use dig to refresh the DNS
exec('/usr/bin/dig dig ' + app.name + '.kite @172.17.42.1 ', function() {}); exec('/usr/bin/dig dig ' + app.name + '.dev @172.17.42.1 ', function() {});
callback(null, container); callback(null, container);
}); });
}); });
}; };
Docker.runContainerSync = function (app, image) { runContainerSync = function (app, image) {
return Meteor._wrapAsync(Docker.runContainer)(app, image); return Meteor._wrapAsync(runContainer)(app, image);
}; };
Docker.restartContainer = function (containerId, callback) { 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) {
@ -95,8 +117,83 @@ Docker.restartContainer = function (containerId, callback) {
}); });
}; };
Docker.restartContainerSync = function (containerId) { restartContainerSync = function (containerId) {
return Meteor._wrapAsync(Docker.restartContainer)(containerId); return Meteor._wrapAsync(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) {
@ -112,7 +209,7 @@ var convertVolumeObjToArray = function (obj) {
return result; return result;
}; };
Docker.getImageData = function (imageId, callback) { 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) {
@ -127,11 +224,11 @@ Docker.getImageData = function (imageId, callback) {
}); });
}; };
Docker.getImageDataSync = function (imageId) { getImageDataSync = function (imageId) {
return Meteor._wrapAsync(Docker.getImageData)(imageId); return Meteor._wrapAsync(getImageData)(imageId);
}; };
Docker.removeImage = function (imageId, callback) { 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; }
@ -140,14 +237,25 @@ Docker.removeImage = function (imageId, callback) {
}); });
}; };
Docker.removeImageSync = function (imageId) { removeImageSync = function (imageId) {
return Meteor._wrapAsync(Docker.removeImage)(imageId); return Meteor._wrapAsync(removeImage)(imageId);
}; };
Docker.removeBindFolder = function (name, callback) { deleteImage = function (image, callback) {
exec(path.join(Util.getBinDir(), 'boot2docker') + ' ssh "sudo rm -rf /var/lib/docker/binds/' + name + '"', function (err, stdout) { if (!image.docker) {
callback(err, stdout); callback(null, {});
}); 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 () {
@ -196,7 +304,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(Util.getBinDir(), 'base-images.tar.gz'), {}, function (err) { docker.loadImage(path.join(getBinDir(), 'base-images.tar.gz'), {}, function (err) {
if (err) { if (err) {
innerCallback(err); innerCallback(err);
return; return;
@ -266,7 +374,7 @@ reloadDefaultContainers = function (callback) {
async.until(function () { async.until(function () {
return ready; return ready;
}, function (callback) { }, function (callback) {
docker.listContainers(function (err) { docker.listContainers(function (err, containers) {
if (!err) { if (!err) {
ready = true; ready = true;
} }
@ -283,7 +391,7 @@ reloadDefaultContainers = function (callback) {
return; return;
} }
console.log('Loading new Kitematic default images.'); console.log('Loading new Kitematic default images.');
docker.loadImage(path.join(Util.getBinDir(), 'base-images.tar.gz'), {}, function (err) { docker.loadImage(path.join(getBinDir(), 'base-images.tar.gz'), {}, function (err) {
if (err) { if (err) {
callback(err); callback(err);
return; return;
@ -392,18 +500,142 @@ 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 {
Docker.removeContainerSync(app.name); removeContainerSync(app.name);
} catch (e) {} } catch (e) {}
try { try {
var container = Docker.runContainerSync(app, image); var container = runContainerSync(app, image);
var containerData = Docker.getContainerDataSync(container.id); var containerData = 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,
@ -418,23 +650,18 @@ 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)();
} }
}); });

View File

@ -2,7 +2,7 @@ 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
VIRTUALBOX_FILE=virtualbox-4.3.12.pkg VIRTUALBOX_FILE=virtualbox-4.3.14.pkg
BOOT2DOCKER_CLI_VERSION=1.2.0 BOOT2DOCKER_CLI_VERSION=1.2.0
BOOT2DOCKER_CLI_VERSION_FILE=boot2docker-$BOOT2DOCKER_CLI_VERSION BOOT2DOCKER_CLI_VERSION_FILE=boot2docker-$BOOT2DOCKER_CLI_VERSION