UNPKG

node-docker

Version:

Node wrapper around the docker REST API - NOTE: alpha status

593 lines (479 loc) 17.1 kB
(function(exports) { // node-docker.js //------------------------------ // // 2013-10-29, Jonas Colmsjö // // Wrapper around the Docker.io REST API // // Using Google JavaScript Style Guide - http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml // //------------------------------ // // In browsers (not a likely scenario): //--------------- // <script type="text/javascript" src="https://raw.github.com/colmsjo/node-docker/master/node-docker.js"></script> // ... // var h = node-docker.create(); // h.test("Testing to use the plates library.", function(){ // ... // // In Node: //--------------- // var node_docker = require('node-docker').create(); /* // Includes // ================ var helpers = require('helpersjs').create(); var fs = require('fs'); var http = require('http'); var async = require('async'); var nconf = require('nconf'); var prettyjson = require('prettyjson'); // Some general setup // ================ // TODO: Should remove logging from module var hostname, port; nconf.use('file', { file: __dirname + '/jacc_config.json' }); nconf.load(); this.hostname = nconf.get('hostname'); this.port = nconf.get('port'); // set logging level switch(nconf.get('logging')) { case 'debug': helpers.logging_threshold = helpers.logging.debug; break; case 'warning': helpers.logging_threshold = helpers.logging.warning; break; default: console.log('error: incorrect logging level in config.json - should be warning or debug!'); } helpers.logDebug('setup: hostname: ' + this.hostname + ' port: ' + this.port); */ "use strict"; exports.create = function() { return { // Properties //============== _imageID : "", _containerID : "", _name : "", _containerPort : null, //previously undefined _use_export : false, _settings : {}, _helpers : require('helpersjs').create(), // helpers //====================================================================== _isset : function(a, message, dontexit){ if (!this._helpers.isset(a)) { console.log(message); if(dontexit !== undefined && dontexit) { return false; } else { return false; // No good - process.exit(); } } this._helpers.logDebug('_isset: returning true '); return true; }, // Docker functions //====================================================================== _dockerRemoteAPI : function(options, funcResData, funcResEnd, funcReq, asyncCallback){ var http = require('http'); if(options.hostname === undefined) { options.hostname = "localhost"; } if(options.port === undefined) { options.port = 4243; } var req = http.request(options, function(res) { res.setEncoding('utf8'); res.on('data', funcResData.bind(this)); if(funcResEnd !== null) { res.on('end', funcResEnd.bind(this)); } else { res.on('end', function () { if(asyncCallback !== undefined) { asyncCallback(null, '_dockerRemoteAPI:'); } }.bind(this)); } }.bind(this)); req.on('error', function(e) { if(asyncCallback !== undefined) { asyncCallback(null, '_dockerRemoteAPI:error'); } console.log("ERROR: Problem doing docker call!"); }.bind()); req.on('end', function(e) { // Doing nothing right now if(asyncCallback !== undefined) { asyncCallback(null, '_dockerRemoteAPI:end'); } }.bind(this)); if(funcReq !== null) { funcReq(req); } else { req.end(); } }, // import //------------------------------------------------------------------------------------------------- // // Equivalent of: curl -H "Content-type: application/tar" --data-binary @webapp.tar http://localhost:4243/build // import : function(asyncCallback){ var options = { path: '/images/create?fromSrc=-', method: 'POST', headers: { 'Content-Type': 'application/tar', } }; var _imageID; this._dockerRemoteAPI(options, function(chunk) { _imageID = JSON.parse(chunk).status; }.bind(this), function() { this._isset(_imageID, 'Import failed! No image was created.'); if(asyncCallback !== undefined) { asyncCallback(null, 'image:'+_imageID); } }.bind(this), function(req) { // write data to the http.ClientRequest (which is a stream) returned by http.request() var fs = require('fs'); var stream; try { stream = fs.createReadStream('webapp.export.tar'); } catch (e) { console.log('ERROR: Could not open webapp.export.tar!'); return; } // Close the request when the stream is closed stream.on('end', function() { req.end(); }.bind(this)); // send the data stream.pipe(req); }.bind(this), asyncCallback); }, // build //------------------------------------------------------------------------------------------------- // // Equivalent of: curl -H "Content-type: application/tar" --data-binary @webapp.tar http://localhost:4243/build // build : function(asyncCallback){ var options = { path: '/build?nocache', method: 'POST', headers: { 'Content-Type': 'application/tar', } }; var _imageID; this._dockerRemoteAPI(options, function(chunk) { console.log('build: ' + chunk); // The last row looks like this 'Successfully built 3df239699c83' if (chunk.slice(0,18) === 'Successfully built') { _imageID = chunk.slice(19,31); } }.bind(this), function() { this._isset(_imageID, 'Build failed! No image was created.'); if(asyncCallback !== undefined) { asyncCallback(null, 'image:'+_imageID); } }.bind(this), function(req) { // write data to the http.ClientRequest (which is a stream) returned by http.request() var fs = require('fs'); var stream; try { stream = fs.createReadStream('webapp.tar'); } catch (e) { console.log('ERROR: Could not open webapp.tar!'); return; } // Close the request when the stream is closed stream.on('end', function() { req.end(); }.bind(this)); // send the data stream.pipe(req); }.bind(this), asyncCallback); return _imageID; }, // createContainer //------------------------------------------------------------------------------------------------- // // create a container with the new image // curl -H "Content-Type: application/json" -d @create.json http://localhost:4243/containers/create // {"Id":"c6bfd6da99d3"} createContainer : function(asyncCallback, imageID){ this._isset(imageID, 'createContainer: imageID not set'); var container = { "Hostname":"", "User":"", "Memory":0, "MemorySwap":0, "AttachStdin":false, "AttachStdout":true, "AttachStderr":true, "PortSpecs":null, "Tty":false, "OpenStdin":false, "StdinOnce":false, "Env":null, "Dns":null, "Image":imageID, "Volumes":{}, "VolumesFrom":"" }; var options = { path: '/containers/create', method: 'POST' }; this._dockerRemoteAPI(options, function(chunk) { // The result should look like this '{"Id":"c6bfd6da99d3"}' try { this._containerID = JSON.parse(chunk).Id; } catch (e) { console.log('Create container failed: '+chunk); asyncCallback(); process.exit(); } }.bind(this), console.log, //null, function(req) { req.write(JSON.stringify(container)); req.end(); }.bind(this), asyncCallback ); }, // start //------------------------------------------------------------------------------------------------- // // Equivalent of: curl -H "Content-Type: application/json" -d @start.json http://localhost:4243/containers/c6bfd6da99d3/start // start : function(asyncCallback){ this._isset(this._containerID, 'start: this._containerID not set'); var binds = { "Binds":["/tmp:/tmp"] }; var options = { path: '/containers/'+this._containerID+'/start', method: 'POST' }; this._dockerRemoteAPI(options, function(chunk) { console.log('start: ' + chunk); }.bind(this), null, function(req) { req.write(JSON.stringify(binds)); req.end(); }.bind(this), asyncCallback ); }, // delete //------------------------------------------------------------------------------------------------- // // Equivalent of: curl -d '' http://localhost:4243/containers/c6bfd6da99d3/stop?t=10 // delete : function(asyncCallback, containerID){ var async = require('async'); var _settings; async.series([ // Get the container ID for the name //function(fn){ this._inspect(fn, containerID); }.bind(this), // Fetch the container settings function(fn) { if (this._isset(containerID, "Container missing, can't inspect", true)) { _settings = this.inspect(fn, containerID); } fn(null, 'second func'); }.bind(this), // stop the container function(fn) { if (this._isset(containerID, "Container missing, can't stop", true)) { var options = { path: '/containers/'+containerID+'/stop?t=10', method: 'POST' }; this._dockerRemoteAPI(options, function(chunk) { this._helpers.logInfo('delete: ' + chunk); }.bind(this), null, null, fn); } fn(null, 'third func'); }.bind(this), // Delete the container function(fn) { if (this._isset(containerID, "Container missing, can't delete", true)) { var options = { path: '/containers/'+containerID+'?v=1', method: 'DELETE' }; this._dockerRemoteAPI(options, function(chunk) { this._helpers.logInfo('delete: ' + chunk); }.bind(this), null, null, fn); } fn(null, 'fourth func'); }.bind(this), // Delete the image function(fn) { if (this._isset(_settings, "Settngs with image ID missing, can't remove",true)) { var options = { path: '/images/'+_settings.Image, method: 'DELETE' }; this._dockerRemoteAPI(options, function(chunk) { this._helpers.logDebug('delete: ' + chunk); }.bind(this), null, null, fn); } fn(null, 'fith func'); }.bind(this), // Finish async series function(fn) { if(asyncCallback !== undefined) { asyncCallback(null, 'delete finished (outer)'); } fn(null, 'delete finished (inner)'); } ]); }, // inspect //------------------------------------------------------------------------------------------------- // // Equivalent of: curl -G http://localhost:4243/containers/c6bfd6da99d3/json // inspect : function(asyncCallback, containerID){ if (!this._isset(containerID, 'inspect: containerID not set', true)) { asyncCallback(null,'inspect not possible without continer'); return; } var options = { path: '/containers/'+containerID+'/json', method: 'GET' }; var _settings; this._dockerRemoteAPI(options, function(chunk) { try { _settings = this._settings = JSON.parse(chunk); this._imageID = this._settings.Image; } catch (e) { console.log('inspect: error fetching data for - ' + containerID); } }.bind(this), null, null, asyncCallback); return _settings; }, // logs //------------------------------------------------------------------------------------------------- // // Get the logs of the started container (should show the current date since that's all the container does) // Equivalent of: curl -H "Content-Type: application/vnd.docker.raw-stream" -d '' "http://localhost:4243/containers/c6bfd6da99d3/attach?logs=1&stream=0&stdout=1" // logs : function(asyncCallback){ this._isset(this._containerID, 'logs: this._containerID not set'); var options = { path: '/containers/'+this._containerID+'/attach?logs=1&stream=0&stdout=1', method: 'POST', headers: { 'Content-Type': 'application/vnd.docker.raw-stream', } }; this._dockerRemoteAPI(options, function(chunk) { console.log('logs: ' + chunk); }, null, null, asyncCallback ); }, // status functions //------------------------------------------------------------------------------------------------- // // Show logs and settings // // status helper function // curl -G http://localhost:4243/containers/json containers : function(asyncCallback) { var options = { path: '/containers/json', method: 'GET', }; var prettyjson = require('prettyjson'); this._dockerRemoteAPI(options, function(chunk) { var containers = JSON.parse(chunk); containers.forEach(function(container) { var containerID = container.Id; this._inspect(asyncCallback, containerID); }); console.log('containers: ' + prettyjson.render(containers)); }, null, null, asyncCallback ); }, status : function(containerID){ var async = require('async'), prettyjson = require('prettyjson'); // List all containers if (containerID === undefined) { async.series([ function(fn){ this._proxyStatus(fn); }.bind(this), /*function(fn){ this._containers(fn); }.bind(this)*/ ], function(err, results){ console.log('status: results of async functions - ' + results); console.log('status: errors (if any) - ' + err); }); } // Show status for a specific container else { async.series([ function(fn){ this._isset(containerID, 'There is no app with the name: '+this._name); this._inspect(fn, containerID); }.bind(this), function(fn){ this._logs(fn, containerID); }.bind(this), function(fn){ console.log(prettyjson.render(this._settings)); fn(null, 'settings printed'); }.bind(this) ], function(err, results){ console.log('status: results of async functions - ' + results); console.log('status: errors (if any) - ' + err); }); } } }; }; }(typeof exports === 'undefined' ? this['node-docker']={} : exports));