guvnor
Version:
A node process manager that isn't spanners all the way down
171 lines (136 loc) • 4.84 kB
JavaScript
var EventEmitter = require('wildemitter')
var util = require('util')
var Autowire = require('wantsit').Autowire
var timeoutify = require('timeoutify')
var LocalDaemonConnection = function (socketName) {
EventEmitter.call(this, {
wildcard: true,
delimiter: ':'
})
this._connected = false
Object.defineProperty(this, 'connected', {
enumerable: true,
get: function () {
return this._connected
}.bind(this)
})
this._socketName = socketName
this._client = null
this._logger = Autowire
this._fs = Autowire
this._dnode = Autowire
this._config = Autowire
}
util.inherits(LocalDaemonConnection, EventEmitter)
LocalDaemonConnection.prototype.disconnect = function (callback) {
if (!this._client || !this.connected) {
this._logger.debug('Not connected to the daemon')
if (callback) {
callback()
}
return
}
this._connected = false
this._logger.debug('Disconnecting from daemon socket', this._socketName)
this._client.once('end', function () {
this._logger.debug('Disconnected from daemon socket', this._socketName)
delete this._client
if (callback) {
callback()
}
}.bind(this))
this._client.stream.end()
this._client.stream.destroy()
this._client.destroy()
}
LocalDaemonConnection.prototype.connect = function (api, callback) {
return this._connect(this._config.guvnor.rundir + '/' + this._socketName, api, callback)
}
LocalDaemonConnection.prototype._connect = function (socket, api, callback) {
this._logger.debug('Connecting to daemon on', socket)
var serverApi = {}
this._client = this._dnode(api, {
timeout: this._config.guvnor.rpctimeout
})
this._client.connect(socket, function (remote) {
this._logger.debug('Connected to daemon on', socket)
for (var method in remote) {
if (typeof (remote[method]) !== 'function') {
continue
}
var func
if (method === 'deployApplication' ||
method === 'removeApplication' ||
method === 'runApplication' ||
method === 'switchApplicationRef' ||
method === 'updateApplicationRefs'
) {
this._logger.debug('Exposing remote method without timeout', method)
func = remote[method].bind(remote)
} else {
this._logger.debug('Timeoutifying remote method', method)
func = timeoutify(remote[method].bind(remote), this._config.guvnor.timeout)
}
func = function (func, method) {
var args = Array.prototype.slice.call(arguments, 2)
var callback = args[args.length - 1]
if (typeof callback !== 'function') {
callback = function () {}
}
if (this._connected) {
// we are connected, invoke remote argument
func.apply(func, args)
} else if (method === 'kill') {
// trying to kill a dead daemon, just invoke the callback
callback()
} else {
// not connected
var error = new Error('Daemon was not running')
error.code = 'DAEMON_NOT_RUNNING'
callback(error)
}
}.bind(this, func, method)
// prepend user information to the remote method invocation
// it's probably dangerous to rely on this because it's easily faked
serverApi[method] = function (func) {
var args = Array.prototype.slice.call(arguments, 1)
args.unshift(process.getuid())
func.apply(func, args)
}.bind(this, func)
}
this._connected = true
callback(undefined, serverApi)
}.bind(this))
this._client.on('error', function (error) {
if (error.code === 'EACCES') {
error.message = "I don't have permission to access " + socket + ' - please run guvnor as a user that can.'
return callback(error)
} else if (error.code === 'ECONNREFUSED') {
this._logger.debug('Connection refused - the socket may be stale')
this._fs.unlink(socket, function (error) {
if (!error) {
this._logger.debug('Removed stale socket file')
error = new Error('Daemon was not running')
error.code = 'DAEMON_NOT_RUNNING'
}
callback(error)
}.bind(this))
return
} else if (error.code === 'ENOENT') {
this._logger.debug('Socket file did not exist - the daemon is probably not running')
error = new Error('Daemon was not running')
error.code = 'DAEMON_NOT_RUNNING'
return callback(error)
} else if (error.code === 'ENOTSOCK') {
return callback(new Error(socket + ' is not a socket - please check the guvnor.rundir property in your config file'))
}
if (this.connected) {
throw error
}
return callback(error)
}.bind(this))
this._client.on('end', function () {
this._connected = false
}.bind(this))
}
module.exports = LocalDaemonConnection