mirror of https://github.com/docker/docs.git
668 lines
17 KiB
JavaScript
Executable File
668 lines
17 KiB
JavaScript
Executable File
Docker = Meteor.require('dockerode');
|
|
|
|
var Convert = Meteor.require('ansi-to-html');
|
|
var convert = new Convert();
|
|
|
|
var DOCKER_HOST='192.168.59.103';
|
|
docker = new Docker({host: '192.168.59.103', port: '2375'});
|
|
|
|
hasDockerfile = function (directory) {
|
|
return fs.existsSync(path.join(directory, 'Dockerfile'));
|
|
};
|
|
|
|
removeContainer = function (containerId, callback) {
|
|
var container = docker.getContainer(containerId);
|
|
container.kill(function (err) {
|
|
if (err) { callback(err); return; }
|
|
container.remove({v:1}, function (err) {
|
|
if (err) { callback(err); return; }
|
|
console.log('Deleted container: ' + containerId);
|
|
callback(null);
|
|
});
|
|
});
|
|
};
|
|
|
|
removeContainerSync = function (containerId) {
|
|
return Meteor._wrapAsync(removeContainer)(containerId);
|
|
};
|
|
|
|
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);
|
|
container.inspect(function (err, data) {
|
|
if (err) {
|
|
callback(err, null);
|
|
return;
|
|
} else {
|
|
data.Config.Volumes = convertVolumeObjToArray(data.Config.Volumes);
|
|
data.Volumes = convertVolumeObjToArray(data.Volumes);
|
|
data.VolumesRW = convertVolumeObjToArray(data.VolumesRW);
|
|
callback(null, data);
|
|
return;
|
|
}
|
|
});
|
|
};
|
|
|
|
getContainerDataSync = function (containerId) {
|
|
return Meteor._wrapAsync(getContainerData)(containerId);
|
|
};
|
|
|
|
runContainer = function (app, image, callback) {
|
|
var envParam = [];
|
|
_.each(_.keys(app.config), function (key) {
|
|
var builtStr = key + '=' + app.config[key];
|
|
envParam.push(builtStr);
|
|
});
|
|
console.log(envParam);
|
|
docker.createContainer({
|
|
Image: image._id.toLowerCase(),
|
|
Tty: false,
|
|
Env: envParam,
|
|
Hostname: app.name,
|
|
name: app.name
|
|
}, function (err, container) {
|
|
if (err) { callback(err, null); return; }
|
|
console.log('Created container: ' + container.id);
|
|
// Bind volumes
|
|
var binds = [];
|
|
if (image.docker.Config.Volumes.length > 0) {
|
|
_.each(image.docker.Config.Volumes, function (vol) {
|
|
binds.push('/var/lib/docker/binds/' + app.name + vol.Path + ':' + vol.Path);
|
|
});
|
|
}
|
|
// Start the container
|
|
container.start({
|
|
PublishAllPorts: true,
|
|
Binds: binds
|
|
}, function (err) {
|
|
if (err) { callback(err, null); return; }
|
|
console.log('Started container: ' + container.id);
|
|
// Use dig to refresh the DNS
|
|
exec('/usr/bin/dig dig ' + app.name + '.dev @172.17.42.1 ', function() {});
|
|
callback(null, container);
|
|
});
|
|
});
|
|
};
|
|
|
|
runContainerSync = function (app, image) {
|
|
return Meteor._wrapAsync(runContainer)(app, image);
|
|
};
|
|
|
|
restartContainer = function (containerId, callback) {
|
|
var container = docker.getContainer(containerId);
|
|
container.restart(function (err) {
|
|
if (err) {
|
|
console.log(err);
|
|
callback(err);
|
|
return;
|
|
}
|
|
console.log('Restarted container: ' + containerId);
|
|
callback(null);
|
|
});
|
|
};
|
|
|
|
restartContainerSync = function (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 result = [];
|
|
if (obj !== null && typeof obj === 'object') {
|
|
_.each(_.keys(obj), function (key) {
|
|
var volumeObj = {};
|
|
volumeObj.Path = key;
|
|
volumeObj.Value = obj[key];
|
|
result.push(volumeObj);
|
|
});
|
|
}
|
|
return result;
|
|
};
|
|
|
|
getImageData = function (imageId, callback) {
|
|
var image = docker.getImage(imageId.toLowerCase());
|
|
image.inspect(function (err, data) {
|
|
if (err) {
|
|
callback(err, null);
|
|
return;
|
|
} else {
|
|
data.Config.Volumes = convertVolumeObjToArray(data.Config.Volumes);
|
|
data.ContainerConfig.Volumes = convertVolumeObjToArray(data.ContainerConfig.Volumes);
|
|
callback(null, data);
|
|
return;
|
|
}
|
|
});
|
|
};
|
|
|
|
getImageDataSync = function (imageId) {
|
|
return Meteor._wrapAsync(getImageData)(imageId);
|
|
};
|
|
|
|
removeImage = function (imageId, callback) {
|
|
var image = docker.getImage(imageId.toLowerCase());
|
|
image.remove({force: true}, function (err) {
|
|
if (err) { callback(err); return; }
|
|
console.log('Deleted image: ' + imageId);
|
|
callback(null);
|
|
});
|
|
};
|
|
|
|
removeImageSync = function (imageId) {
|
|
return Meteor._wrapAsync(removeImage)(imageId);
|
|
};
|
|
|
|
deleteImage = function (image, callback) {
|
|
if (!image.docker) {
|
|
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 () {
|
|
return [
|
|
{
|
|
Image: 'kite-dns',
|
|
name: 'kite-dns',
|
|
PortBindings: {'53/udp': [{ 'HostPort': '53', 'HostIp': '172.17.42.1' }]},
|
|
Binds: ['/var/run/docker.sock:/tmp/docker.sock']
|
|
}
|
|
];
|
|
};
|
|
|
|
checkDefaultImages = function (callback) {
|
|
var defaultNames = defaultContainerOptions().map(function (container) {
|
|
return container.name;
|
|
});
|
|
async.each(defaultNames, function (name, innerCallback) {
|
|
var image = docker.getImage(name);
|
|
image.inspect(function (err) {
|
|
if (err) {
|
|
if (err.reason === 'no such image') {
|
|
innerCallback('no such image');
|
|
} else {
|
|
innerCallback(err);
|
|
}
|
|
} else {
|
|
innerCallback();
|
|
}
|
|
});
|
|
}, function (err) {
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
callback();
|
|
}
|
|
});
|
|
};
|
|
|
|
resolveDefaultImages = function () {
|
|
var defaultNames = defaultContainerOptions().map(function (container) {
|
|
return container.name;
|
|
});
|
|
async.each(defaultNames, function (name, innerCallback) {
|
|
var image = docker.getImage(name);
|
|
image.inspect(function (err) {
|
|
if (err) {
|
|
if (err.reason === 'no such image') {
|
|
docker.loadImage(path.join(getBinDir(), 'base-images.tar.gz'), {}, function (err) {
|
|
if (err) {
|
|
innerCallback(err);
|
|
return;
|
|
} else {
|
|
innerCallback();
|
|
}
|
|
});
|
|
} else {
|
|
innerCallback(err);
|
|
}
|
|
} else {
|
|
innerCallback();
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
checkDefaultContainers = function(callback) {
|
|
var defaultNames = defaultContainerOptions().map(function (container) {
|
|
return container.name;
|
|
});
|
|
async.each(defaultNames, function (name, innerCallback) {
|
|
var container = docker.getContainer(name);
|
|
container.inspect(function (err, data) {
|
|
if (err) {
|
|
innerCallback(err);
|
|
} else {
|
|
if (data && data.State && data.State.Running) {
|
|
innerCallback(null);
|
|
} else {
|
|
innerCallback('Not running');
|
|
}
|
|
}
|
|
});
|
|
}, function (err) {
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
callback();
|
|
}
|
|
});
|
|
};
|
|
|
|
resolveDefaultContainers = function (callback) {
|
|
var defaultNames = defaultContainerOptions().map(function (container) {
|
|
return container.name;
|
|
});
|
|
killAndRemoveContainers(defaultNames, function (err) {
|
|
if (err) {
|
|
callback(err);
|
|
return;
|
|
}
|
|
upContainers(defaultContainerOptions(), function (err) {
|
|
callback(err);
|
|
});
|
|
});
|
|
};
|
|
|
|
reloadDefaultContainers = function (callback) {
|
|
console.log('Reloading default containers.');
|
|
|
|
var defaultNames = defaultContainerOptions().map(function (container) {
|
|
return container.name;
|
|
});
|
|
|
|
var ready = false;
|
|
async.until(function () {
|
|
return ready;
|
|
}, function (callback) {
|
|
docker.listContainers(function (err, containers) {
|
|
if (!err) {
|
|
ready = true;
|
|
}
|
|
callback();
|
|
});
|
|
}, function (err) {
|
|
console.log(err);
|
|
console.log('Removing old Kitematic default containers.');
|
|
killAndRemoveContainers(defaultNames, function (err) {
|
|
console.log('Removed old Kitematic default containers.');
|
|
if (err) {
|
|
console.log('Removing old Kitematic default containers ERROR.');
|
|
callback(err);
|
|
return;
|
|
}
|
|
console.log('Loading new Kitematic default images.');
|
|
docker.loadImage(path.join(getBinDir(), 'base-images.tar.gz'), {}, function (err) {
|
|
if (err) {
|
|
callback(err);
|
|
return;
|
|
}
|
|
console.log('Starting new Kitematic default containers.');
|
|
upContainers(defaultContainerOptions(), function (err) {
|
|
callback(err);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
upContainers = function (optionsList, callback) {
|
|
var createDefaultContainer = function (options, innerCallback) {
|
|
docker.createContainer(options, function (err, container) {
|
|
if (err) {
|
|
innerCallback(err);
|
|
return;
|
|
}
|
|
container.start({
|
|
PublishAllPorts: true,
|
|
PortBindings: options.PortBindings,
|
|
Binds: options.Binds
|
|
}, function (err) {
|
|
innerCallback(err);
|
|
});
|
|
});
|
|
};
|
|
|
|
async.each(optionsList, function (options, innerCallback) {
|
|
var container = docker.getContainer(options.name);
|
|
container.inspect(function (err, data) {
|
|
if (err) {
|
|
if (err.reason.indexOf('no such container') !== -1) {
|
|
createDefaultContainer(options, function (err) {
|
|
innerCallback(err);
|
|
});
|
|
} else {
|
|
innerCallback(err);
|
|
}
|
|
} else {
|
|
if (data && !data.State.Running) {
|
|
container.start(function (err) {
|
|
innerCallback(err);
|
|
});
|
|
} else {
|
|
innerCallback();
|
|
}
|
|
}
|
|
});
|
|
}, function (err) {
|
|
callback(err);
|
|
});
|
|
};
|
|
|
|
removeImages = function (names, callback) {
|
|
async.each(names, function (name, innerCallback) {
|
|
var image = docker.getImage(name);
|
|
image.remove(function (err) {
|
|
if (err) {
|
|
console.log('remove image error');
|
|
console.log(err);
|
|
if (err.reason === 'no such image') {
|
|
innerCallback();
|
|
} else {
|
|
innerCallback(err);
|
|
}
|
|
} else {
|
|
innerCallback();
|
|
}
|
|
});
|
|
}, function (err) {
|
|
callback(err);
|
|
});
|
|
};
|
|
|
|
killAndRemoveContainers = function (names, callback) {
|
|
async.each(names, function (name, innerCallback) {
|
|
var container = docker.getContainer(name);
|
|
container.inspect(function (err, data) {
|
|
if (err) {
|
|
innerCallback();
|
|
return;
|
|
}
|
|
if (data.State.Running) {
|
|
// Kill it
|
|
container.kill(function (err) {
|
|
if (err) {
|
|
innerCallback(err);
|
|
} else {
|
|
// Remove it
|
|
container.remove(function (err) {
|
|
innerCallback(err);
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
container.remove(function (err) {
|
|
innerCallback(err);
|
|
});
|
|
}
|
|
});
|
|
}, function (err) {
|
|
callback(err);
|
|
});
|
|
};
|
|
|
|
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({
|
|
runApp: function (app) {
|
|
this.unblock();
|
|
var image = Images.findOne({_id: app.imageId});
|
|
try {
|
|
removeContainerSync(app.name);
|
|
} catch (e) {}
|
|
try {
|
|
var container = runContainerSync(app, image);
|
|
var containerData = getContainerDataSync(container.id);
|
|
Meteor.setTimeout(function () {
|
|
Apps.update(app._id, {$set: {
|
|
docker: containerData,
|
|
status: 'READY'
|
|
}});
|
|
}, 2500);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
},
|
|
getDockerHost: function () {
|
|
return DOCKER_HOST;
|
|
},
|
|
reloadDefaultContainers: function () {
|
|
return Meteor._wrapAsync(reloadDefaultContainers)();
|
|
},
|
|
checkDefaultImages: function () {
|
|
return Meteor._wrapAsync(checkDefaultImages)();
|
|
},
|
|
resolveDefaultImages: function () {
|
|
return Meteor._wrapAsync(resolveDefaultImages)();
|
|
},
|
|
checkDefaultContainers: function () {
|
|
return Meteor._wrapAsync(checkDefaultContainers)();
|
|
},
|
|
resolveDefaultContainers: function () {
|
|
return Meteor._wrapAsync(resolveDefaultContainers)();
|
|
}
|
|
});
|