UNPKG

jacc

Version:

Just Another Cloud in the Cloud

322 lines (231 loc) 8.61 kB
exports = exports ? this # # 2013-11-03 Jonas Colmsjö # # Jacc - simple PaaS based on docker.io, hipache, redis-dns and supervisord # # Commands: # + list - show list of configured apps # + status - show running apps # + add - add new app # + delete - delete app # # Using Google JavaScript Style Guide when applicable - http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml # exports.create = () -> _commands : '[add|delete|list|update|status|help]' # THIS GAVE PROBLEMS? , version: 'v1.1' _dockerConnOptions : { socketPath: false, host: 'http://localhost', port: '4243'} # Includes # ====================================================================== # node_docker : require('node-docker').create() async : require('async') redis : require('redis') # _ : require('underscore') # varaibles used within async needs to be defined like this # _helpers : require('helpersjs').create() # helpers # ====================================================================== _isset : (a, message, dontexit) -> # if (!this._helpers.isset(a)) if (!a?) console.log(message) if(dontexit != undefined && dontexit) return false else process.exit() return true # Redis helper # ------------------------------------------------------------- # operation = ”keys” | ”get” | ”del” | ”push” | "set" # args = [arg1, arg2, …, argn] # func = function to run on the result _f : (rc, err, res, func) => if (err) throw err if(func != null) func(res) rc.quit() _redis : (operation, args, func) -> if(!args instanceof Array) throw new Error('_redis: argument not an array') redis_client = this.redis.createClient() redis_client.on("connect", () => switch operation when "keys" then redis_client.keys( args[0], (err, res) => this._f(redis_client, err, res, func) ) when "get" then redis_client.get( args[0], (err, res) => this._f(redis_client, err, res, func) ) when "del" then redis_client.del( args[0], (err, res) => this._f(redis_client, err, res, func) ) when "rpush" then redis_client.rpush( args[0], args[1], (err, res) => this._f(redis_client, err, res, func) ) when "set" then redis_client.set( args[0], args[1], (err, res) => this._f(redis_client, err, res, func) ) when "smembers" then redis_client.smembers( args[0], (err, res) => this._f(redis_client, err, res, func) ) when "sadd" then redis_client.sadd( args[0], args[1], (err, res) => this._f(redis_client, err, res, func) ) when "lrange" then redis_client.lrange( args[0], args[1], args[2], (err, res) => this._f(redis_client, err, res, func) ) else throw Error('_redis: unsuported operation') ) # Helper for running function on each jacc image # ------------------------------------------------------------- # redis jacc config: jacc_images:”012345678912” -> {URL, internal_port, DNS} _onJaccConfig : (func, endFunc) -> this._redis( "smembers", ["images"], (res) => if(res.length==0) console.log("EMPTY JACC CONFIG!") else this.async.each( res (item, fn) => if(item?) func(item, fn) () => endFunc() if endFunc? ) ) # Run function with runing containers as input # ---------------------------------------------------------------------- _onContainers : (func, endFunc) -> # all options listed in the REST documentation for Docker are supported. _options = {} this._containers = {}; # docker = require('../vendor/docker.io')( this._dockerConnOptions ) docker = require('docker.io')( this._dockerConnOptions ) # list the running containers docker.containers.list( _options, (err, res) => if (err) endFunc() if endFunc? throw err if (res.length==0) endFunc() if endFunc? else # inspect each running container this.async.each(res, (container, fn) => _options = {} docker.containers.inspect(container.Id, _options, (err, res) => if (err) throw err func(res) fn() ) () => endFunc() if endFunc? ) endFunc() if endFunc? ) # Jacc Functions # ====================================================================== # Update redis-dns and hipache configuration # ---------------------------------------------------------------------- # NOTE: Only one container per image is currently supported _listImages : (endFunc) -> # Build list with running images # runningImages = image id->[{container id, IP}] this._runningImages = {} this._onContainers( (res) => # create empty list if this image isn’t running if( this._runningImages[ res.Image[0..11] ] == undefined) this._runningImages[ res.Image[0..11] ] = [] this._runningImages[ res.Image[0..11] ].push( { ID: res.ID[0..11], IP: res.NetworkSettings.IPAddress } ) endFunc ) _buildHipacheConfig : (endFunc) -> # Iterate over Jacc configuration and generate hipache and redis-dns configuration # hipache configuration: image id ->external URL & [internal URL] # redis-dns configuration: dns->IP this._onJaccConfig( (image, fn) => this._redis("get", [image], (res) => # decomposing, just to make sure things are ok {URL, internal_port, DNS} = JSON.parse(res) if(!this._runningImages[ image ]?) console.log("Image "+ image + " lacks running containers") fn() else # Set redis-dns config, use the first IP in the list this._redis( "set", [ DNS, this._runningImages[ image ][0]["IP"] ], ()=> # Set hipache config _key = "frontend:"+URL this._redis("del", [_key], () => this._redis("rpush", [_key, image], () => this.async.each( this._runningImages[ image ], (res, fn2) => this._redis("rpush", [ _key, 'http://'+res["IP"]+ ':'+internal_port ], fn2) fn ) ) ) ) ) endFunc ) update : () -> this._listImages( ()=> this._buildHipacheConfig() ) list : () -> console.log("Jacc: current configuration") this._onJaccConfig( (item, endFunc) => this._redis("get", [item], (res) => console.log(JSON.stringify(item)+" - "+JSON.stringify(res)) endFunc() ) null ) # Show running containers # ---------------------------------------------------------------------- status : (fn) -> console.log("Jacc: List of running containers") this._onContainers( (res) => console.log("container:" + res.ID[0..12] + " image:" + res.Image[0..12] + " IP:" + res.NetworkSettings.IPAddress) fn ) # Add image to Jacc configuration # ---------------------------------------------------------------------- # # TODO: Should check that the image exists (do an inspect) add : (image, URL, internal_port, dns_name, fn) -> console.log("Jacc: adding " + image + " - remeber to do 'jacc update'") this._redis( "sadd", ["images", image], (res) => _args = [image, JSON.stringify({URL: URL; internal_port: internal_port; DNS: dns_name})] this._redis("set", _args, (res) => fn() if fn? ) ) # Delete image from Jacc configuration # ---------------------------------------------------------------------- # # TODO: Should check that the image exists (do an inspect) delete : (image) -> console.log("Jacc: deleting " + image) redis_client = this.redis.createClient() redis_client.on("connect", () => redis_client.srem("images", image, (err, res) => console.log("result - " + res) redis_client.quit() ) ) # main # ====================================================================== main : () -> argv = require('optimist') .usage('Usage: $0 '+ this._commands) .argv this._isset(argv._ , 'jacc requires a command - node app.js ' + this._commands) switch argv._[0] when "add" then this.add(argv._[1], argv._[2], argv._[3], argv._[4]) when "delete" then this.delete(argv._[1]) when "update" then this.update() when "status" then this.status() when "list" then this.list() when "help" console.log('usage: jacc ' + this._commands) console.log('jacc add image URL port dns-name (exmaple: jacc add 123456789 www.example.com 80 example.local)') console.log('jacc delete image') console.log('jacc update') console.log('jacc list') console.log('jacc status') console.log('help: show this message') else console.log('No such jacc command: ' + argv._[0] + ' try jacc help!')