cloudelements-petit
Advanced tools
Sorry, the diff of this file is not supported yet
+17
-16
| { | ||
| "name": "TrustFileForQB", | ||
| "provider": "Avalara", | ||
| "display": "1.0.1", | ||
| "version": "0.5.10", | ||
| "name": "Ground2Cloud", | ||
| "provider": "CloudElements", | ||
| "display": "0.6.29", | ||
| "version": "0.6.29", | ||
| "ui": { | ||
| "logColors": { | ||
| "debug": "gray", | ||
| "info": "#0587BE", | ||
| "info": "black", | ||
| "warn": "orange", | ||
@@ -16,14 +16,15 @@ "error": "red", | ||
| "install": { | ||
| "host": "g2c.cloud-elements.com", | ||
| "brand": "avalara", | ||
| "name": "TrustFileForQB", | ||
| "shortName": "TrustFileForQB", | ||
| "shortNameLc": "trustfileforqb", | ||
| "dir": "TrustFileForQB", | ||
| "fullDir": "C:\\TrustFileForQB", | ||
| "outputName": "TrustFileForQBSetup", | ||
| "qbOutputName": "TrustFileForQBSetup", | ||
| "url": "http://www.avalara.com/", | ||
| "brand": "avalara" | ||
| "eula": true, | ||
| "host": "g2c-staging.cloud-elements.com", | ||
| "brand": "ce", | ||
| "name": "Ground2Cloud", | ||
| "shortName": "Ground2Cloud", | ||
| "shortNameLc": "ground2cloud", | ||
| "dir": "Ground2Cloud", | ||
| "fullDir": "C:\\Ground2Cloud", | ||
| "outputName": "Ground2CloudSetup", | ||
| "qbOutputName": "Ground2CloudQbSetup", | ||
| "url": "http://www.cloud-elements.com/", | ||
| "brand": "ce" | ||
| } | ||
| } |
+1
-1
@@ -1,1 +0,1 @@ | ||
| title TrustFile for QB GUI | ||
| title Ground2Cloud GUI |
@@ -1,5 +0,5 @@ | ||
| h3 Welcome2! | ||
| h3 Welcome! | ||
| p.vp | ||
| | This is the TrustFile for QB GUI, version | ||
| | This is Cloud Elements Ground2Cloud branded GUI, version | ||
| span.version: i loading | ||
@@ -9,11 +9,12 @@ | ||
| You can choose one of the above tabs to view current logs, edit the | ||
| TrustFile for QB configuration, or perform management operations (such | ||
| as starting, stopping or re-registering the client). | ||
| Ground2Cloud configuration, or perform management operations (such as | ||
| starting, stopping or re-registering the client). | ||
| p | ||
| | If you have any trouble with this application, or any other product, | ||
| | visit our website at | ||
| | trustfile.avalara.com | ||
| | If you have any trouble with this application, or any other Cloud | ||
| | Elements product, please reach us at: help@cloud-elements.com, or | ||
| | visit our website at | ||
| | cloud-elements.com | ||
| p.warn | ||
+1
-2
@@ -1,2 +0,1 @@ | ||
| Copyright (c) 2015, 2016 Cloud Elements, Inc. | ||
| All rights reserved. | ||
| Copyright (c) 2015-2018 Cloud Elements, Inc. All rights reserved. |
+2
-2
| { | ||
| "name": "cloudelements-petit", | ||
| "version": "0.6.16", | ||
| "version": "0.6.29", | ||
| "description": "A Cloud Elements http proxy, named after Phillipe Petit", | ||
@@ -36,3 +36,3 @@ "main": "lib/index.js", | ||
| "lodash": "latest", | ||
| "mkdirp": "latest", | ||
| "mkdirp": "0.5.1", | ||
| "node-forge": "latest", | ||
@@ -39,0 +39,0 @@ "node-getopt": "latest", |
| (function() { | ||
| var _, async, checkPass0, config, connect, createClientVitae, crypto, events, forge, fs, handshake, mkdirp, net, os, patches, path, pingPass0, registerPass0, registerPass1, registerPass2, registerPass3, registerPass4, streams, toml, unregisterPass0, unregisterPass1, unregisterPass2, upgradePass0, util; | ||
| _ = require('lodash'); | ||
| async = require('async'); | ||
| crypto = require('crypto'); | ||
| events = require('events'); | ||
| forge = require('node-forge'); | ||
| fs = require('fs'); | ||
| mkdirp = require('mkdirp'); | ||
| net = require('net'); | ||
| os = require('os'); | ||
| path = require('path'); | ||
| patches = require('./patches'); | ||
| util = require('./util'); | ||
| streams = require('../streams'); | ||
| config = require('../config'); | ||
| toml = require('toml'); | ||
| createClientVitae = function(cfgData) { | ||
| var ref, vitae; | ||
| vitae = _.cloneDeep((ref = cfgData.vitae) != null ? ref : {}); | ||
| _.assign(vitae, { | ||
| hostname: os.hostname(), | ||
| arch: os.arch(), | ||
| platform: os.platform(), | ||
| release: os.release(), | ||
| version: config.version().version | ||
| }); | ||
| return vitae; | ||
| }; | ||
| exports.create = function() { | ||
| var protocol; | ||
| protocol = new events.EventEmitter(); | ||
| protocol.connect = function(lt) { | ||
| return connect(protocol, lt, true); | ||
| }; | ||
| protocol.open = function(cfgData) { | ||
| return connect(protocol, { | ||
| config: cfgData | ||
| }, false); | ||
| }; | ||
| return protocol; | ||
| }; | ||
| connect = function(protocol, lt, doShake) { | ||
| var conn, ended, handler; | ||
| protocol.status = {}; | ||
| protocol.config = lt.config; | ||
| _.defaults(protocol.config, { | ||
| host: 'localhost', | ||
| backPort: 3014 | ||
| }); | ||
| ended = false; | ||
| protocol.end = function() {}; | ||
| protocol.connection = handler = (function() { | ||
| var handle, handlerStack, io, push, readAccumulate, write; | ||
| handlerStack = []; | ||
| readAccumulate = ''; | ||
| return { | ||
| push: push = function(handler) { | ||
| if (!_.isFunction(handler)) { | ||
| return protocol.emit('error', "Non-functional handler " + handler); | ||
| } else { | ||
| return handlerStack.push(handler); | ||
| } | ||
| }, | ||
| write: write = function(data) { | ||
| return conn.write(JSON.stringify(data) + '\x03'); | ||
| }, | ||
| draw: function(sdata) { | ||
| var etx, rdata, results; | ||
| readAccumulate += sdata; | ||
| results = []; | ||
| while ((etx = readAccumulate.indexOf('\x03')) !== -1) { | ||
| rdata = readAccumulate.substr(0, etx); | ||
| readAccumulate = readAccumulate.substr(etx + 1); | ||
| results.push(handle(rdata)); | ||
| } | ||
| return results; | ||
| }, | ||
| handle: handle = function(sdata) { | ||
| var data, e; | ||
| try { | ||
| data = JSON.parse(sdata); | ||
| } catch (error) { | ||
| e = error; | ||
| return protocol.emit('error', "JSON parse error: " + e); | ||
| } | ||
| if (handlerStack.length === 0) { | ||
| return protocol.emit('error', "No handler left"); | ||
| } | ||
| return handlerStack.shift()(data); | ||
| }, | ||
| io: io = function(out, handler) { | ||
| push(handler); | ||
| return write(out); | ||
| }, | ||
| expect: function(out, expectedType, handler, errHandler) { | ||
| return io(out, function(data) { | ||
| if (data.type === 'error') { | ||
| protocol.emit('error', data.error); | ||
| return typeof errHandler === "function" ? errHandler(data) : void 0; | ||
| } else if (data.type !== expectedType) { | ||
| protocol.emit('error', "Expected " + expectedType + ", got " + data.type); | ||
| return typeof errHandler === "function" ? errHandler(data) : void 0; | ||
| } else { | ||
| return handler(data); | ||
| } | ||
| }); | ||
| }, | ||
| handleRaw: function(cb) { | ||
| conn.removeListener('data', handler.draw); | ||
| conn.pause(); | ||
| conn.setEncoding(null); | ||
| delete conn._readableState.decoder; | ||
| return cb(conn, function() { | ||
| conn.setEncoding('utf8'); | ||
| conn.on('data', handler.draw); | ||
| return conn.resume(); | ||
| }); | ||
| } | ||
| }; | ||
| })(); | ||
| conn = new net.Socket(); | ||
| conn.setEncoding('utf8'); | ||
| conn.on('error', function(err) { | ||
| return protocol.emit('error', "Conn err: " + err + " " + err.stack); | ||
| }); | ||
| conn.on('data', handler.draw); | ||
| conn.on('close', function(hadErr) { | ||
| return protocol.emit('close', hadErr); | ||
| }); | ||
| return conn.connect(protocol.config.backPort, protocol.config.host, function() { | ||
| protocol.end = function() { | ||
| if (!ended) { | ||
| conn.end(); | ||
| } | ||
| return ended = true; | ||
| }; | ||
| protocol.register = function(tname) { | ||
| return registerPass0(protocol, handler, tname); | ||
| }; | ||
| protocol.unregister = function(regId) { | ||
| return unregisterPass0(protocol, handler, regId); | ||
| }; | ||
| protocol.ping = function(cb) { | ||
| return pingPass0(protocol, handler, cb); | ||
| }; | ||
| protocol.check = function(cb) { | ||
| return checkPass0(protocol, handler, cb); | ||
| }; | ||
| protocol.upgrade = function(cb, dstat) { | ||
| return upgradePass0(protocol, handler, cb, dstat); | ||
| }; | ||
| protocol.emit('connected'); | ||
| if (doShake) { | ||
| return handshake(protocol, handler); | ||
| } | ||
| }); | ||
| }; | ||
| handshake = function(protocol, handler) { | ||
| var cfgData, key, receive0, receive1, receive2, regIds; | ||
| cfgData = protocol.config; | ||
| regIds = _.map(cfgData.registration, function(r) { | ||
| return r.id; | ||
| }); | ||
| key = cfgData.keydata; | ||
| receive0 = function() { | ||
| var send1; | ||
| send1 = { | ||
| type: 'handshake1', | ||
| handshakeVersion: 1, | ||
| registeredId: regIds | ||
| }; | ||
| return handler.expect(send1, 'shakehand1', receive1); | ||
| }; | ||
| receive1 = function(data) { | ||
| var err, resp, send2; | ||
| try { | ||
| resp = key != null ? crypto.privateDecrypt(key, new Buffer(data.prompt, 'base64')) : new Buffer(data.prompt, 'base64'); | ||
| } catch (error) { | ||
| err = error; | ||
| protocol.emit('error', "Unable to decrypt server prompt. (Is there a key mismatch?)"); | ||
| return protocol.end(); | ||
| } | ||
| send2 = { | ||
| type: 'handshake2', | ||
| handshakeVersion: 1, | ||
| registeredId: regIds, | ||
| response: resp.toString('base64') | ||
| }; | ||
| return handler.expect(send2, 'shakehand2', receive2); | ||
| }; | ||
| receive2 = function(data) { | ||
| var bindPort, ep, fn, i, len, ref, ref1, reg; | ||
| if (cfgData.server == null) { | ||
| cfgData.server = {}; | ||
| } | ||
| cfgData.server.vitae = data.vitae; | ||
| bindPort = data.bindPort; | ||
| if (!(_.isArray(bindPort))) { | ||
| bindPort = [bindPort]; | ||
| } | ||
| if ((ep = (ref = cfgData.server.vitae) != null ? ref.endpoint : void 0) != null) { | ||
| cfgData.server.vitae.endpoints = {}; | ||
| ref1 = cfgData.registration; | ||
| fn = function(reg) { | ||
| var ep1, epath, epath2; | ||
| ep1 = ep.replace(/REGID/g, reg.id); | ||
| cfgData.server.vitae.endpoints[reg.id] = ep1; | ||
| reg.endpoint = ep1; | ||
| epath = path.join(reg.dir, 'endpoint'); | ||
| fs.writeFile(epath, ep1 + "\n", { | ||
| encoding: 'utf8' | ||
| }, function(err) { | ||
| return protocol.emit('save', err, epath); | ||
| }); | ||
| epath2 = path.join(path.dirname(cfgData.configPath), 'endpoint'); | ||
| return fs.writeFile(epath2, ep1 + "\n", { | ||
| encoding: 'utf8' | ||
| }, function(err) { | ||
| return protocol.emit('save', err, epath2); | ||
| }); | ||
| }; | ||
| for (i = 0, len = ref1.length; i < len; i++) { | ||
| reg = ref1[i]; | ||
| fn(reg); | ||
| } | ||
| } | ||
| if (cfgData.registration.length !== bindPort.length) { | ||
| protocol.emit('error', "length mismatch: " + bindPort.length + ", " + cfgData.registration.length); | ||
| return protocol.end(); | ||
| } | ||
| return async.map(_.zip(cfgData.registration, bindPort), function(arg, cb) { | ||
| var bp, r, tCfgPath, tenant; | ||
| r = arg[0], bp = arg[1]; | ||
| tenant = _.assign(_.clone(r), { | ||
| bindPort: bp | ||
| }); | ||
| tCfgPath = path.join(r.dir, 'config.toml'); | ||
| return fs.exists(tCfgPath, function(exists) { | ||
| if (exists) { | ||
| return fs.readFile(tCfgPath, { | ||
| encoding: 'utf8' | ||
| }, function(err, tCfgData) { | ||
| var e, obj; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| try { | ||
| obj = toml.parse(tCfgData); | ||
| } catch (error) { | ||
| e = error; | ||
| return cb(e); | ||
| } | ||
| tenant.config = obj; | ||
| return cb(null, tenant); | ||
| }); | ||
| } else { | ||
| return cb(null, tenant); | ||
| } | ||
| }); | ||
| }, function(err, tenants) { | ||
| var send3, vitae; | ||
| if (err != null) { | ||
| protocol.emit('error', "Bad tenant configuration: " + err); | ||
| return protocol.end(); | ||
| } | ||
| vitae = createClientVitae(cfgData); | ||
| send3 = { | ||
| type: 'handshake3', | ||
| vitae: vitae | ||
| }; | ||
| protocol.status.handshake = { | ||
| tenants: tenants | ||
| }; | ||
| handler.write(send3); | ||
| return protocol.emit('handshake'); | ||
| }); | ||
| }; | ||
| return receive0(); | ||
| }; | ||
| registerPass0 = function(protocol, handler, tname) { | ||
| var cfgData; | ||
| cfgData = protocol.config; | ||
| return config.unregisteredTenants(protocol.config, function(err, tenPath, opts) { | ||
| var index; | ||
| if (err != null) { | ||
| return protocol.emit('error', err); | ||
| } | ||
| if (tname) { | ||
| index = opts.indexOf(tname); | ||
| if (index > -1) { | ||
| return registerPass1(protocol, handler, path.join(tenPath, opts[index], 'registration')); | ||
| } else { | ||
| return protocol.emit('error', "No unregistered tenant " + tname + "."); | ||
| } | ||
| } else { | ||
| if (opts.length > 0) { | ||
| return registerPass1(protocol, handler, path.join(tenPath, opts[0], 'registration')); | ||
| } else { | ||
| return config.lease(cfgData, function(err, newten) { | ||
| if (err != null) { | ||
| return protocol.emit('error', err); | ||
| } | ||
| return registerPass1(protocol, handler, path.join(newten, 'registration')); | ||
| }); | ||
| } | ||
| } | ||
| }); | ||
| }; | ||
| registerPass1 = function(protocol, handler, regPath) { | ||
| var cfgData, keyPath; | ||
| cfgData = protocol.config; | ||
| keyPath = path.join(path.dirname(cfgData.configPath), 'key'); | ||
| return fs.exists(keyPath, function(exists) { | ||
| if (exists) { | ||
| return fs.readFile(keyPath, { | ||
| encoding: 'utf8' | ||
| }, function(err, keydata) { | ||
| if (err != null) { | ||
| return protocol.emit('error', err); | ||
| } | ||
| return registerPass2(protocol, handler, regPath, keyPath, keydata); | ||
| }); | ||
| } else { | ||
| return registerPass2(protocol, handler, regPath, keyPath); | ||
| } | ||
| }); | ||
| }; | ||
| registerPass2 = function(protocol, handler, regPath, keyPath, keyData) { | ||
| var nmDir, primeScript, prv, pub; | ||
| if (keyData == null) { | ||
| protocol.emit('keygen start'); | ||
| nmDir = path.dirname(path.dirname(path.dirname(__filename))); | ||
| if (path.basename(path.dirname(nmDir)) === 'node_modules') { | ||
| nmDir = path.dirname(nmDir); | ||
| } else { | ||
| nmDir = path.join(nmDir, 'node_modules'); | ||
| } | ||
| primeScript = path.join(nmDir, 'node-forge', 'js', 'prime.worker.js'); | ||
| return forge.pki.rsa.generateKeyPair({ | ||
| bits: 2048, | ||
| workers: -1, | ||
| workerScript: primeScript | ||
| }, function(err, pair) { | ||
| var privatePem; | ||
| if (err != null) { | ||
| return protocol.emit('error', err); | ||
| } | ||
| privatePem = forge.pki.privateKeyToPem(pair.privateKey); | ||
| return fs.writeFile(keyPath, privatePem, { | ||
| encoding: 'utf8' | ||
| }, function(err) { | ||
| if (err != null) { | ||
| return protocol.emit('error', err); | ||
| } | ||
| return fs.chmod(keyPath, 0x180, function(err) { | ||
| if (err != null) { | ||
| return protocol.emit('error', err); | ||
| } | ||
| protocol.emit('keygen end'); | ||
| return registerPass3(protocol, handler, regPath, pair); | ||
| }); | ||
| }); | ||
| }); | ||
| } else { | ||
| prv = forge.pki.privateKeyFromPem(keyData); | ||
| pub = forge.pki.setRsaPublicKey(prv.n, prv.e); | ||
| return registerPass3(protocol, handler, regPath, { | ||
| publicKey: pub, | ||
| privateKey: prv | ||
| }); | ||
| } | ||
| }; | ||
| registerPass3 = function(protocol, handler, regPath, pair) { | ||
| var cfgData, prefPath; | ||
| cfgData = protocol.config; | ||
| prefPath = path.join(path.dirname(regPath), 'preferred'); | ||
| return fs.exists(prefPath, function(exists) { | ||
| if (exists) { | ||
| return fs.readFile(prefPath, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| if (err != null) { | ||
| return protocol.emit('error', err); | ||
| } | ||
| return registerPass4(protocol, handler, regPath, pair, prefPath, JSON.parse(data.trim())); | ||
| }); | ||
| } else { | ||
| return registerPass4(protocol, handler, regPath, pair, prefPath, null); | ||
| } | ||
| }); | ||
| }; | ||
| registerPass4 = function(protocol, handler, regPath, pair, prefPath, pref) { | ||
| var receive0, receive1; | ||
| receive0 = function() { | ||
| var send1; | ||
| send1 = { | ||
| type: 'register1', | ||
| handshakeVersion: 1, | ||
| publicKey: forge.pki.publicKeyToPem(pair.publicKey), | ||
| preferred: pref | ||
| }; | ||
| return handler.expect(send1, 'didRegister1', receive1); | ||
| }; | ||
| receive1 = function(data) { | ||
| var regData, regId, regPort; | ||
| regId = data.registeredId; | ||
| regPort = data.registeredPort; | ||
| regData = JSON.stringify({ | ||
| id: regId, | ||
| port: regPort | ||
| }); | ||
| return fs.writeFile(regPath, regData, { | ||
| encoding: 'utf8' | ||
| }, function(err) { | ||
| var prefData; | ||
| if (err != null) { | ||
| return protocol.emit('error', err); | ||
| } | ||
| prefData = JSON.stringify({ | ||
| id: regId, | ||
| port: regPort | ||
| }); | ||
| return fs.writeFile(prefPath, prefData + "\n", { | ||
| encoding: 'utf8' | ||
| }, function(err) { | ||
| if (err != null) { | ||
| return protocol.emit('error', err); | ||
| } | ||
| handler.write({ | ||
| type: 'register2' | ||
| }); | ||
| return protocol.emit('registered', regPath); | ||
| }); | ||
| }); | ||
| }; | ||
| return receive0(); | ||
| }; | ||
| unregisterPass0 = function(protocol, handler, regId) { | ||
| var cfgData, tenPath; | ||
| cfgData = protocol.config; | ||
| tenPath = path.join(path.dirname(cfgData.configPath), 'tenants'); | ||
| return mkdirp(tenPath, function(err) { | ||
| if (err != null) { | ||
| return protocol.emit('error', err); | ||
| } | ||
| return fs.readdir(tenPath, function(err, dirs) { | ||
| var regObj; | ||
| regObj = null; | ||
| return async.filter(dirs, function(dir, cb) { | ||
| var regPath; | ||
| regPath = path.join(tenPath, dir, 'registration'); | ||
| return fs.exists(regPath, function(exists) { | ||
| if (!exists) { | ||
| return cb(null, false); | ||
| } | ||
| return fs.readFile(regPath, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| var e, obj; | ||
| if (err != null) { | ||
| return protocol.emit('error', err); | ||
| } | ||
| try { | ||
| obj = JSON.parse(data.trim()); | ||
| } catch (error) { | ||
| e = error; | ||
| return protocol.emit('error', e); | ||
| } | ||
| if (obj.id === regId) { | ||
| regObj = obj; | ||
| return cb(null, true); | ||
| } else { | ||
| return cb(null, false); | ||
| } | ||
| }); | ||
| }); | ||
| }, function(err, opts) { | ||
| if (err != null) { | ||
| return protocol.emit('error', err); | ||
| } | ||
| if (opts.length > 1) { | ||
| return protocol.emit('error', "Too many regs found for " + regId + "."); | ||
| } else if (opts.length < 1) { | ||
| return protocol.emit('error', "No tenant found with id " + regId + "."); | ||
| } else { | ||
| return unregisterPass1(protocol, handler, regObj, path.join(tenPath, opts[0], 'registration')); | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| unregisterPass1 = function(protocol, handler, regdata, regPath) { | ||
| var cfgData, keyPath; | ||
| cfgData = protocol.config; | ||
| keyPath = path.join(path.dirname(cfgData.configPath), 'key'); | ||
| return fs.readFile(keyPath, { | ||
| encoding: 'utf8' | ||
| }, function(err, keydata) { | ||
| if (err != null) { | ||
| return protocol.emit('error', err); | ||
| } | ||
| return unregisterPass2(protocol, handler, regdata, regPath, keydata); | ||
| }); | ||
| }; | ||
| unregisterPass2 = function(protocol, handler, regdata, regPath, key) { | ||
| var cfgData, receive0, receive1, receive1err, receive2, receive2err, regId, unlinkRegKey; | ||
| cfgData = protocol.config; | ||
| regId = regdata.id; | ||
| receive0 = function() { | ||
| var send1; | ||
| send1 = { | ||
| type: 'unregister1', | ||
| handshakeVersion: 1, | ||
| registeredId: regId | ||
| }; | ||
| return handler.expect(send1, 'didUnregister1', receive1, receive1err); | ||
| }; | ||
| receive1 = function(data) { | ||
| var err, resp, send2; | ||
| try { | ||
| resp = crypto.privateDecrypt(key, new Buffer(data.prompt, 'base64')); | ||
| } catch (error) { | ||
| err = error; | ||
| protocol.emit('error', "Unable to decrypt server prompt. (Is there a key mismatch?): removing registration anyways."); | ||
| unlinkRegKey(); | ||
| } | ||
| send2 = { | ||
| type: 'unregister2', | ||
| handshakeVersion: 1, | ||
| registeredId: regId, | ||
| response: resp.toString('base64') | ||
| }; | ||
| return handler.expect(send2, 'didUnregister2', receive2, receive2err); | ||
| }; | ||
| receive1err = function(data) { | ||
| return unlinkRegKey(); | ||
| }; | ||
| unlinkRegKey = function() { | ||
| return fs.unlink(regPath, function(err) { | ||
| var keyPath; | ||
| if (err != null) { | ||
| protocol.emit('error', new Error("Couldn't remove file \"" + regPath + "\".")); | ||
| return protocol.end(); | ||
| } | ||
| keyPath = path.join(path.dirname(cfgData.configPath), 'key'); | ||
| return config.registeredTenants(cfgData, function(err, tenPath, dirs) { | ||
| if (dirs.length === 0) { | ||
| return fs.unlink(keyPath, function(err) { | ||
| if (err != null) { | ||
| protocol.emit('error', new Error("Couldn't remove file \"" + keyPath + "\".")); | ||
| return protocol.end(); | ||
| } | ||
| handler.write({ | ||
| type: 'unregister3' | ||
| }); | ||
| return protocol.emit('unregistered', regPath); | ||
| }); | ||
| } else { | ||
| handler.write({ | ||
| type: 'unregister3' | ||
| }); | ||
| return protocol.emit('unregistered', regPath); | ||
| } | ||
| }); | ||
| }); | ||
| }; | ||
| receive2 = function(data) { | ||
| return unlinkRegKey(); | ||
| }; | ||
| receive2err = function(data) { | ||
| return unlinkRegKey(); | ||
| }; | ||
| return receive0(); | ||
| }; | ||
| pingPass0 = function(protocol, handler, cb) { | ||
| var receive0, receive1; | ||
| receive0 = function() { | ||
| var send1; | ||
| send1 = { | ||
| type: 'ping1' | ||
| }; | ||
| return handler.io(send1, receive1); | ||
| }; | ||
| receive1 = function(data) { | ||
| return cb(null, data); | ||
| }; | ||
| return receive0(); | ||
| }; | ||
| checkPass0 = function(protocol, handler, cb) { | ||
| var cfgData, receive0, receive1; | ||
| cfgData = protocol.config; | ||
| receive0 = function() { | ||
| var send1, vitae; | ||
| vitae = createClientVitae(cfgData); | ||
| send1 = { | ||
| type: 'check1', | ||
| vitae: vitae | ||
| }; | ||
| return handler.expect(send1, 'didCheck1', receive1); | ||
| }; | ||
| receive1 = function(data) { | ||
| return cb(null, data); | ||
| }; | ||
| return receive0(); | ||
| }; | ||
| upgradePass0 = function(protocol, handler, cb, dstat) { | ||
| var cfgData, receive0, receive1; | ||
| cfgData = protocol.config; | ||
| receive0 = function() { | ||
| var send1, vitae; | ||
| vitae = createClientVitae(cfgData); | ||
| send1 = { | ||
| type: 'check1', | ||
| vitae: vitae | ||
| }; | ||
| return handler.expect(send1, 'didCheck1', receive1); | ||
| }; | ||
| receive1 = function(data) { | ||
| var patch, send2, size; | ||
| if (!data.patch.update) { | ||
| return cb(null, { | ||
| size: 0 | ||
| }); | ||
| } | ||
| patch = data.patch; | ||
| size = data.patch.size; | ||
| if (size == null) { | ||
| return cb(new Error('No size found')); | ||
| } | ||
| if (size === 0) { | ||
| return cb(null, { | ||
| size: size | ||
| }); | ||
| } | ||
| handler.handleRaw(function(conn, done) { | ||
| return patches.makeDest(cfgData, function(err, dest) { | ||
| var outstream, taker, tdone; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| outstream = dest.outstream(); | ||
| tdone = function() { | ||
| return outstream.end(function() { | ||
| done(); | ||
| return cb(null, { | ||
| patch: patch, | ||
| path: dest.path() | ||
| }); | ||
| }); | ||
| }; | ||
| taker = streams.take(conn, size, tdone, dstat); | ||
| return taker.pipe(outstream); | ||
| }); | ||
| }); | ||
| send2 = { | ||
| type: 'upgrade1', | ||
| version: data.patch.version, | ||
| id: data.patch.id | ||
| }; | ||
| return handler.write(send2); | ||
| }; | ||
| return receive0(); | ||
| }; | ||
| }).call(this); |
| (function() { | ||
| var backtalk, connectBacktalk, connectListener, connectTunnel, disconnectBacktalk, doListen, emitter, events, forge, listen, resetProtocol, start, tunnel, waitfor; | ||
| forge = require('node-forge'); | ||
| events = require('events'); | ||
| emitter = require('../emitter'); | ||
| listen = require('../listen'); | ||
| waitfor = require('../waitfor'); | ||
| backtalk = require('./backtalk'); | ||
| tunnel = require('./tunnel'); | ||
| resetProtocol = function(protocol) { | ||
| return protocol.backchannel.ping = function(cb) { | ||
| return cb("Ping not available"); | ||
| }; | ||
| }; | ||
| exports.create = function(daemon) { | ||
| var protocol, resetHandle, resetRetry, retryDelays, retryHandle, retryIndex, retryWith; | ||
| protocol = new events.EventEmitter(); | ||
| emitter.augment(protocol); | ||
| protocol.daemon = daemon; | ||
| protocol.listener = new events.EventEmitter(); | ||
| protocol.backchannel = new events.EventEmitter(); | ||
| protocol.tunnel = new events.EventEmitter(); | ||
| resetProtocol(protocol); | ||
| resetHandle = null; | ||
| retryHandle = null; | ||
| retryDelays = [2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 15, 15, 15, 15, 15, 15, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 300, 300, 300, 300, 300, 300, 300]; | ||
| retryIndex = 0; | ||
| retryWith = function(configData) { | ||
| protocol._running = false; | ||
| if (resetHandle != null) { | ||
| clearTimeout(resetHandle); | ||
| resetHandle = null; | ||
| } | ||
| if (!protocol._wanting) { | ||
| if (retryHandle != null) { | ||
| clearTimeout(retryHandle); | ||
| return retryHandle = null; | ||
| } | ||
| } else { | ||
| protocol.emit('willRetry', retryIndex, retryDelays[retryIndex]); | ||
| return retryHandle = setTimeout((function() { | ||
| protocol.emit('retry', retryIndex); | ||
| retryHandle = null; | ||
| retryIndex = Math.min(retryIndex + 1, retryDelays.length - 1); | ||
| if (protocol._wanting) { | ||
| return protocol.start(configData); | ||
| } | ||
| }), retryDelays[retryIndex] * 1000); | ||
| } | ||
| }; | ||
| resetRetry = function() { | ||
| if (resetHandle != null) { | ||
| clearTimeout(resetHandle); | ||
| } | ||
| if (retryHandle != null) { | ||
| clearTimeout(retryHandle); | ||
| } | ||
| resetHandle = null; | ||
| retryHandle = null; | ||
| return retryIndex = 0; | ||
| }; | ||
| protocol._running = false; | ||
| protocol.start = function(configData, cb) { | ||
| var onstart; | ||
| if (protocol._running) { | ||
| return typeof cb === "function" ? cb() : void 0; | ||
| } | ||
| protocol._running = true; | ||
| protocol._wanting = true; | ||
| onstart = function() { | ||
| resetHandle = setTimeout(resetRetry, 5000); | ||
| return typeof cb === "function" ? cb() : void 0; | ||
| }; | ||
| protocol.once('start', onstart); | ||
| protocol.once('close', function() { | ||
| resetProtocol(protocol); | ||
| protocol.removeListener('start', onstart); | ||
| return retryWith(configData); | ||
| }); | ||
| return start(protocol, configData); | ||
| }; | ||
| protocol.end = function(cb) { | ||
| protocol._wanting = false; | ||
| resetRetry(); | ||
| if (!protocol._running) { | ||
| return typeof cb === "function" ? cb() : void 0; | ||
| } | ||
| if (cb != null) { | ||
| protocol.once('close', cb); | ||
| } | ||
| return protocol.emit('ending'); | ||
| }; | ||
| protocol.restart = function(configData, cb) { | ||
| return protocol.end(function() { | ||
| return protocol.start(configData, cb); | ||
| }); | ||
| }; | ||
| protocol._listening = false; | ||
| protocol.listen = function(configData, cb) { | ||
| var onlistening; | ||
| if (protocol._listening) { | ||
| return typeof cb === "function" ? cb() : void 0; | ||
| } | ||
| protocol.config = configData; | ||
| protocol._listening = true; | ||
| onlistening = function() { | ||
| return typeof cb === "function" ? cb() : void 0; | ||
| }; | ||
| protocol.once('listening', onlistening); | ||
| protocol.once('deaf', function() { | ||
| protocol.removeListener('listening', onlistening); | ||
| return protocol._listening = false; | ||
| }); | ||
| return doListen(protocol, configData); | ||
| }; | ||
| protocol.deafen = function(cb) { | ||
| if (!protocol._listening) { | ||
| return typeof cb === "function" ? cb() : void 0; | ||
| } | ||
| if (cb != null) { | ||
| protocol.once('deaf', cb); | ||
| } | ||
| return protocol.emit('deafening'); | ||
| }; | ||
| protocol.relisten = function(configData, cb) { | ||
| return protocol.deafen(function() { | ||
| return protocol.listen(configData, cb); | ||
| }); | ||
| }; | ||
| protocol.status = function() { | ||
| var configData, fingerprint, privateKey, ref, ref1, ref2, serverVitae, status, vitae; | ||
| if (protocol._listening) { | ||
| if (protocol._running) { | ||
| configData = protocol.config; | ||
| vitae = (ref = configData.vitae) != null ? ref : {}; | ||
| serverVitae = (ref1 = (ref2 = configData.server) != null ? ref2.vitae : void 0) != null ? ref1 : {}; | ||
| if (configData.keydata != null) { | ||
| privateKey = forge.pki.privateKeyFromPem(configData.keydata); | ||
| fingerprint = forge.ssh.getPublicKeyFingerprint(privateKey, { | ||
| encoding: 'hex', | ||
| delimiter: ':' | ||
| }); | ||
| } | ||
| status = { | ||
| status: 'running', | ||
| registration: configData.registration, | ||
| fingerprint: fingerprint, | ||
| vitae: vitae, | ||
| server: { | ||
| vitae: serverVitae | ||
| } | ||
| }; | ||
| return status; | ||
| } else { | ||
| return { | ||
| status: 'listening', | ||
| vitae: vitae, | ||
| server: { | ||
| vitae: serverVitae | ||
| } | ||
| }; | ||
| } | ||
| } else { | ||
| return { | ||
| status: 'stopped' | ||
| }; | ||
| } | ||
| }; | ||
| return protocol; | ||
| }; | ||
| start = function(protocol, configData) { | ||
| var waiter; | ||
| waiter = waitfor.create(function(isErr) { | ||
| return protocol.emit('close', isErr); | ||
| }); | ||
| disconnectBacktalk(protocol, protocol.listener.lt, waiter); | ||
| return connectBacktalk(protocol, configData, protocol.listener.lt, waiter); | ||
| }; | ||
| doListen = function(protocol, configData) { | ||
| var waiter; | ||
| waiter = waitfor.create(function(isErr) { | ||
| return protocol.emit('deaf', isErr); | ||
| }); | ||
| return connectListener(protocol, configData, waiter); | ||
| }; | ||
| connectListener = function(protocol, configData, waiter) { | ||
| var lt; | ||
| lt = listen.create(); | ||
| emitter.augment(lt); | ||
| protocol.listener.lt = lt; | ||
| lt.on('error', function(err) { | ||
| protocol.listener.emit('error', err); | ||
| protocol.emit('error', err); | ||
| return waiter.error(err); | ||
| }); | ||
| lt.on('close', function() { | ||
| protocol.listener.emit('close'); | ||
| return waiter.close(); | ||
| }); | ||
| lt.on('connection', function(ltc) { | ||
| return protocol.listener.emit('connection', ltc); | ||
| }); | ||
| lt.on('command', function(cmd, ltc) { | ||
| protocol.listener.emit('command', cmd, ltc); | ||
| return protocol.emit('command', cmd, ltc); | ||
| }); | ||
| lt.on('listening', function(addr) { | ||
| waiter.open(); | ||
| protocol.listener.emit('listening', addr); | ||
| return protocol.emit('listening', addr); | ||
| }); | ||
| if (protocol._listening) { | ||
| protocol.on('deafening', function() { | ||
| return lt.end(); | ||
| }); | ||
| return lt.listen(configData, 3015); | ||
| } else { | ||
| return waiter.close(); | ||
| } | ||
| }; | ||
| disconnectBacktalk = function(protocol, lt, waiter) { | ||
| protocol.unlisten(); | ||
| return lt.unlisten(); | ||
| }; | ||
| connectBacktalk = function(protocol, configData, lt, waiter) { | ||
| var bt; | ||
| bt = backtalk.create(); | ||
| protocol.backchannel.ping = function(cb) { | ||
| if (bt.ping != null) { | ||
| return bt.ping(cb); | ||
| } else { | ||
| return cb("Ping not available"); | ||
| } | ||
| }; | ||
| bt.on('error', function(err) { | ||
| protocol.backchannel.emit('error', err); | ||
| protocol.emit('error', err); | ||
| waiter.error(err); | ||
| return bt.end(); | ||
| }); | ||
| bt.on('close', function(hadErr) { | ||
| protocol.backchannel.emit('close', hadErr); | ||
| return waiter.close(); | ||
| }); | ||
| bt.on('connected', function() { | ||
| return protocol.backchannel.emit('connected'); | ||
| }); | ||
| bt.on('save', function(err, msg) { | ||
| return protocol.backchannel.emit('save', err, msg); | ||
| }); | ||
| bt.on('handshake', function() { | ||
| var i, len, ref, ref1, ref2, results, tenant; | ||
| protocol.backchannel.emit('handshake'); | ||
| waiter.open(); | ||
| ref2 = (ref = (ref1 = bt.status.handshake) != null ? ref1.tenants : void 0) != null ? ref : []; | ||
| results = []; | ||
| for (i = 0, len = ref2.length; i < len; i++) { | ||
| tenant = ref2[i]; | ||
| results.push(connectTunnel(protocol, lt, bt, tenant, waiter.branch())); | ||
| } | ||
| return results; | ||
| }); | ||
| lt.listenOn('close', function() { | ||
| return bt.end(); | ||
| }); | ||
| if (protocol._running) { | ||
| protocol.listenOn('ending', function() { | ||
| return bt.end(); | ||
| }); | ||
| return bt.connect(lt); | ||
| } else { | ||
| return waiter.close(); | ||
| } | ||
| }; | ||
| connectTunnel = function(protocol, lt, bt, tenant, waiter) { | ||
| var ct; | ||
| ct = tunnel.create(); | ||
| ct.on('error', function(err) { | ||
| protocol.tunnel.emit('error', err); | ||
| protocol.emit('error', err); | ||
| waiter.error(err); | ||
| return ct.end(); | ||
| }); | ||
| ct.on('close', function() { | ||
| protocol.tunnel.emit('close'); | ||
| bt.end(); | ||
| return waiter.close(); | ||
| }); | ||
| ct.on('received', function(ctc) { | ||
| return protocol.tunnel.emit('received', ctc); | ||
| }); | ||
| bt.on('close', function() { | ||
| return ct.end(); | ||
| }); | ||
| lt.listenOn('close', function() { | ||
| return ct.end(); | ||
| }); | ||
| ct.on('connected', function() { | ||
| protocol.tunnel.emit('connected'); | ||
| return waiter.open(); | ||
| }); | ||
| protocol.emit('start'); | ||
| if (protocol._running) { | ||
| protocol.listenOn('ending', function() { | ||
| return ct.end(); | ||
| }); | ||
| return ct.connect(bt, tenant); | ||
| } else { | ||
| return waiter.close(); | ||
| } | ||
| }; | ||
| }).call(this); |
| (function() { | ||
| var _, async, config, doHeal, editPreferred, events, fs, manage, path, postDelete, postKill, postSaved, postUnload, postUnregister, psnode, regIdResetTry, util; | ||
| _ = require('lodash'); | ||
| async = require('async'); | ||
| config = require('../config'); | ||
| events = require('events'); | ||
| fs = require('fs'); | ||
| manage = require('./manage'); | ||
| path = require('path'); | ||
| util = require('./util'); | ||
| psnode = require('ps-node'); | ||
| regIdResetTry = 30000; | ||
| exports.heal = function(cfgData, daemon) { | ||
| var output, recovPath; | ||
| output = daemon.output; | ||
| output.log("Trying to heal connection."); | ||
| recovPath = path.join(path.dirname(cfgData.configPath), 'healing'); | ||
| return fs.exists(recovPath, function(exists) { | ||
| if (!exists) { | ||
| return doHeal(cfgData, daemon, recovPath); | ||
| } | ||
| return fs.readFile(recovPath, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| var e, now, recovData; | ||
| if (err != null) { | ||
| output.warn("Error reading " + recovPath + ": proceeding anyways"); | ||
| return doHeal(cfgData, daemon, recovPath); | ||
| } | ||
| try { | ||
| recovData = JSON.parse(data.trim()); | ||
| } catch (error) { | ||
| e = error; | ||
| output.warn("Error parsing " + recovPath + ": " + e + ": proceeding anyways"); | ||
| return doHeal(cfgData, daemon, recovPath); | ||
| } | ||
| now = new Date().getTime(); | ||
| if (!(_.isNumber(recovData != null ? recovData.lastAttempt : void 0))) { | ||
| output.warn("last heal attempt " + (recovData != null ? recovData.lastAttempt : void 0) + " is not a number: proceeding anyways"); | ||
| return doHeal(cfgData, daemon, recovPath); | ||
| } | ||
| if (now - recovData.lastAttempt < 600000) { | ||
| return output.log("last heal attempt " + recovData.lastAttempt + " not too long ago: skipping."); | ||
| } else { | ||
| return doHeal(cfgData, daemon, recovPath, recovData); | ||
| } | ||
| }); | ||
| }); | ||
| }; | ||
| doHeal = function(cfgData, daemon, recovPath, recovData) { | ||
| var attempts, now, output, ref, str; | ||
| output = daemon.output; | ||
| output.log("Healing connection."); | ||
| if (recovData != null) { | ||
| now = new Date().getTime(); | ||
| attempts = ((ref = recovData.attempts) != null ? ref : 0) + 1; | ||
| if (now - recovData.lastAttempt < 3600000) { | ||
| attempts = 1; | ||
| } | ||
| recovData.lastAttempt = new Date().getTime(); | ||
| recovData.attempts = attempts; | ||
| } else { | ||
| recovData = { | ||
| lastAttempt: new Date().getTime(), | ||
| attempts: 1 | ||
| }; | ||
| } | ||
| str = JSON.stringify(recovData, null, 2) + '\n'; | ||
| return fs.writeFile(recovPath, str, { | ||
| encoding: 'utf8' | ||
| }, function(err) { | ||
| var singlePostUnload, singleton; | ||
| if (err != null) { | ||
| return output.warn("can't write recovery data to " + recovPath + ": " + err + ": aborting"); | ||
| } | ||
| singleton = false; | ||
| singlePostUnload = function(fromTimeout) { | ||
| return function() { | ||
| if (singleton) { | ||
| return; | ||
| } | ||
| singleton = true; | ||
| if (fromTimeout) { | ||
| output.log("daemon unload timed out: proceeding anyways"); | ||
| } | ||
| return postUnload(cfgData, daemon, recovData); | ||
| }; | ||
| }; | ||
| daemon.unloadNoUnlock(singlePostUnload(false)); | ||
| return setTimeout(singlePostUnload(true), 10000); | ||
| }); | ||
| }; | ||
| postUnload = function(cfgData, daemon, recovData) { | ||
| var output, savePath; | ||
| output = daemon.output; | ||
| output.log("Healer saving existing tenants."); | ||
| savePath = path.join(path.dirname(cfgData.configPath), 'restore'); | ||
| return config.registeredTenants(cfgData, function(err, tenPath, dirs) { | ||
| var str; | ||
| str = JSON.stringify(dirs, null, 2) + '\n'; | ||
| return fs.writeFile(savePath, str, { | ||
| encoding: 'utf8' | ||
| }, function(err) { | ||
| if (err != null) { | ||
| output.warn("couldn't write 'restore' file: " + err); | ||
| } | ||
| return postSaved(cfgData, daemon, recovData); | ||
| }); | ||
| }); | ||
| }; | ||
| postSaved = function(cfgData, daemon, recovData) { | ||
| var handler, opts, output, ref, singlePostUnregister, singleton; | ||
| output = daemon.output; | ||
| output.log("Healer unregistering tenants."); | ||
| singleton = false; | ||
| singlePostUnregister = function(fromTimeout) { | ||
| return function() { | ||
| if (singleton) { | ||
| return; | ||
| } | ||
| singleton = true; | ||
| if (fromTimeout) { | ||
| output.log("Healer unregister timed out: proceeding anyways"); | ||
| } | ||
| return postUnregister(cfgData, daemon, recovData); | ||
| }; | ||
| }; | ||
| opts = { | ||
| options: { | ||
| config: cfgData.configPath, | ||
| all: true | ||
| } | ||
| }; | ||
| handler = new events.EventEmitter(); | ||
| handler.on('success', function(obj) { | ||
| return singlePostUnregister(false)(); | ||
| }); | ||
| handler.on('failure', function(obj) { | ||
| switch (obj.type) { | ||
| case 'argv': | ||
| output.warn("Bad manager usage: " + obj.error + "."); | ||
| break; | ||
| case 'config': | ||
| output.warn("Couldn't read config \"" + obj.path + "\"."); | ||
| break; | ||
| case 'exists': | ||
| output.warn("Client isn't registered."); | ||
| break; | ||
| case 'message': | ||
| output.warn("Couldn't unregister " + obj.path + "."); | ||
| break; | ||
| default: | ||
| output.warn("Unknown unregister error " + obj); | ||
| } | ||
| output.warn("Proceeding anyways."); | ||
| return singlePostUnregister(false)(); | ||
| }); | ||
| manage.commands(handler).unregister.run(opts); | ||
| return setTimeout(singlePostUnregister(true), 5000 + 7000 * ((ref = cfgData.registration) != null ? ref : []).length); | ||
| }; | ||
| postUnregister = function(cfgData, daemon, recovData) { | ||
| var output; | ||
| output = daemon.output; | ||
| output.log("Healer " + process.pid + " killing other node clients."); | ||
| return psnode.lookup({ | ||
| command: 'node', | ||
| "arguments": 'clientd' | ||
| }, function(err, list) { | ||
| return async.eachSeries(list, function(proc, cb) { | ||
| if (("" + proc.pid) !== ("" + process.pid)) { | ||
| output.log("Healer killing " + proc.pid); | ||
| return psnode.kill(proc.pid, function(err) { | ||
| if (err != null) { | ||
| output.warn("Healer can't kill process " + proc.pid + ": " + err); | ||
| } | ||
| return cb(null); | ||
| }); | ||
| } else { | ||
| return cb(null); | ||
| } | ||
| }, function(err) { | ||
| if (err != null) { | ||
| output.warn("Healer problem killing processes: " + err); | ||
| } | ||
| return postKill(cfgData, daemon, recovData); | ||
| }); | ||
| }); | ||
| }; | ||
| postKill = function(cfgData, daemon, recovData) { | ||
| var output; | ||
| output = daemon.output; | ||
| output.log("Healer editing / deleting files"); | ||
| return daemon.unlock(function() { | ||
| var cfgDir, deleteFiles, names; | ||
| cfgDir = path.dirname(cfgData.configPath); | ||
| names = ['client.lock', 'client.pid', 'key', 'registration', 'endpoint']; | ||
| deleteFiles = function() { | ||
| return async.eachSeries(names, function(name, cb) { | ||
| var fpath; | ||
| fpath = path.join(cfgDir, name); | ||
| return fs.exists(fpath, function(exists) { | ||
| if (!exists) { | ||
| return cb(null); | ||
| } | ||
| output.log("Healer deleting file " + fpath); | ||
| return fs.unlink(fpath, function(err) { | ||
| if (err != null) { | ||
| output.warn("healer cannot remove " + fpath + ": " + err); | ||
| } | ||
| return cb(null); | ||
| }); | ||
| }); | ||
| }, function(err) { | ||
| var doAllTens; | ||
| if (err != null) { | ||
| output.warn("healer error " + err); | ||
| } | ||
| doAllTens = function() { | ||
| return config.allTenants(cfgData, function(err, tenPath, tens) { | ||
| if (err != null) { | ||
| output.warn("can't access tenants " + err); | ||
| postDelete(cfgData, daemon, recovData); | ||
| } | ||
| return async.eachSeries(tens, function(ten, cb) { | ||
| return async.eachSeries(names, function(name, cb) { | ||
| var fpath; | ||
| fpath = path.join(ten.dir, name); | ||
| return fs.exists(fpath, function(exists) { | ||
| if (!exists) { | ||
| return cb(null); | ||
| } | ||
| output.log("Healer deleting file " + fpath); | ||
| return fs.unlink(fpath, function(err) { | ||
| if (err != null) { | ||
| output.warn("healer can't remove " + fpath + ": " + err); | ||
| } | ||
| return cb(null); | ||
| }); | ||
| }); | ||
| }, function(err) { | ||
| if (err != null) { | ||
| output.warn("healer error " + err); | ||
| } | ||
| if (names.indexOf('preferred') === -1) { | ||
| return editPreferred(output, path.join(ten.dir, 'preferred'), cb); | ||
| } else { | ||
| return cb(err); | ||
| } | ||
| }); | ||
| }, function(err) { | ||
| if (err != null) { | ||
| output.warn("healer tenant error " + err); | ||
| } | ||
| return postDelete(cfgData, daemon, recovData); | ||
| }); | ||
| }); | ||
| }; | ||
| if (names.indexOf('preferred') === -1) { | ||
| return editPreferred(output, path.join(cfgDir, 'preferred'), function(err) { | ||
| if (err != null) { | ||
| output.warn("healer preferred err " + err); | ||
| } | ||
| return doAllTens(); | ||
| }); | ||
| } else { | ||
| return doAllTens(); | ||
| } | ||
| }); | ||
| }; | ||
| return deleteFiles(); | ||
| }); | ||
| }; | ||
| editPreferred = function(output, prefPath, cb) { | ||
| output.log("Healer editing preferred file " + prefPath); | ||
| return fs.exists(prefPath, function(exists) { | ||
| if (!exists) { | ||
| return cb(null); | ||
| } | ||
| return fs.readFile(prefPath, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| var e, obj, str; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| try { | ||
| obj = JSON.parse(data); | ||
| } catch (error) { | ||
| e = error; | ||
| return cb(e); | ||
| } | ||
| delete obj.port; | ||
| str = JSON.stringify(obj, null, 2) + '\n'; | ||
| return fs.writeFile(prefPath, str, { | ||
| encoding: 'utf8' | ||
| }, cb); | ||
| }); | ||
| }); | ||
| }; | ||
| postDelete = function(cfgData, daemon, recovData) { | ||
| var output; | ||
| output = daemon.output; | ||
| output.log("Healer restarting."); | ||
| util.restart(daemon.service); | ||
| return process.exit(-1); | ||
| }; | ||
| }).call(this); |
-1074
| (function() { | ||
| var _, async, backtalk, childs, commands, config, events, forge, fs, lock, net, path, rimraf, util, | ||
| slice = [].slice; | ||
| _ = require('lodash'); | ||
| async = require('async'); | ||
| childs = require('child_process'); | ||
| events = require('events'); | ||
| forge = require('node-forge'); | ||
| fs = require('fs'); | ||
| net = require('net'); | ||
| path = require('path'); | ||
| rimraf = require('rimraf'); | ||
| config = require('../config'); | ||
| lock = require('../lock'); | ||
| backtalk = require('./backtalk'); | ||
| util = require('./util'); | ||
| exports.commands = commands = function(emitter) { | ||
| var evictRun, fail, multi, part, stop, succ, unregisterRun; | ||
| succ = function(obj) { | ||
| return emitter.emit('success', obj); | ||
| }; | ||
| fail = function(obj) { | ||
| return emitter.emit('failure', obj); | ||
| }; | ||
| multi = function(m) { | ||
| return emitter.emit('multi', m); | ||
| }; | ||
| part = function() { | ||
| var p; | ||
| p = 1 <= arguments.length ? slice.call(arguments, 0) : []; | ||
| return emitter.emit.apply(emitter, ['part'].concat(slice.call(p))); | ||
| }; | ||
| return { | ||
| init: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| return config.createConfig(opts.options.config, function(err, made, path) { | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'config', | ||
| error: err, | ||
| path: path | ||
| }); | ||
| } | ||
| if (made) { | ||
| return succ({ | ||
| path: path | ||
| }); | ||
| } else { | ||
| return fail({ | ||
| type: 'exists', | ||
| path: path | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| }, | ||
| start: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var configPath; | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return config.readConfig(configPath, function(err, configPath, data) { | ||
| var exe, noder; | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'config', | ||
| path: configPath, | ||
| error: err | ||
| }); | ||
| } | ||
| exe = path.join(path.dirname(fs.realpathSync(__filename)), 'petit-clientd-bin') + path.extname(__filename); | ||
| noder = path.extname(exe) === '.coffee' ? 'coffee' : 'node'; | ||
| return lock.findDeep('client', configPath, true, function(err, exists, data) { | ||
| var ch, checkgo, totalTime; | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'lock', | ||
| error: err | ||
| }); | ||
| } | ||
| if (exists) { | ||
| return fail({ | ||
| type: 'exists', | ||
| pid: data.pid | ||
| }); | ||
| } | ||
| ch = childs.spawn(noder, [exe, '-c', configPath], { | ||
| detached: true, | ||
| stdio: 'ignore' | ||
| }); | ||
| ch.once('error', function(err) { | ||
| return fail({ | ||
| type: 'message', | ||
| message: "Unable to start client.", | ||
| error: err | ||
| }); | ||
| }); | ||
| ch.unref(); | ||
| totalTime = 0; | ||
| checkgo = function() { | ||
| return lock.find('client', configPath, function(err, exists, data) { | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'postlock', | ||
| error: err | ||
| }); | ||
| } | ||
| if (exists) { | ||
| return succ({ | ||
| pid: data.pid | ||
| }); | ||
| } else if (totalTime > 4000) { | ||
| return fail({ | ||
| type: 'postfind' | ||
| }); | ||
| } else { | ||
| totalTime += 200; | ||
| return setTimeout(checkgo, 200); | ||
| } | ||
| }); | ||
| }; | ||
| return setTimeout(checkgo, 200); | ||
| }); | ||
| }); | ||
| } | ||
| }, | ||
| stop: stop = { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var configPath, isKilled, waitKilled; | ||
| configPath = config.pickConfig(opts.options.config); | ||
| lock.find('client', configPath, function(err, exists, data) { | ||
| var conn; | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'lock', | ||
| error: err | ||
| }); | ||
| } | ||
| if (!exists) { | ||
| return fail({ | ||
| type: 'exists' | ||
| }); | ||
| } | ||
| conn = new net.Socket(); | ||
| conn.setEncoding('utf8'); | ||
| conn.on('close', function() { | ||
| return waitKilled(data.pid, function(killed) { | ||
| if (killed) { | ||
| return succ({ | ||
| sigint: false | ||
| }); | ||
| } | ||
| process.kill(data.pid, 'SIGINT'); | ||
| return waitKilled(data.pid, function(killed) { | ||
| if (killed) { | ||
| return succ({ | ||
| sigint: true | ||
| }); | ||
| } else { | ||
| return fail({ | ||
| type: 'unstop', | ||
| pid: data.pid | ||
| }); | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| return conn.connect(data.listen, 'localhost', function() { | ||
| return conn.end('STOP\x03'); | ||
| }); | ||
| }); | ||
| waitKilled = function(pid, cb) { | ||
| var maybeDone, repeater, total; | ||
| if (isKilled(pid)) { | ||
| return cb(true); | ||
| } | ||
| total = 0; | ||
| maybeDone = function() { | ||
| if (isKilled(pid)) { | ||
| clearInterval(repeater); | ||
| return cb(true); | ||
| } else if (total < 10) { | ||
| return ++total; | ||
| } else { | ||
| clearInterval(repeater); | ||
| return cb(false); | ||
| } | ||
| }; | ||
| return repeater = setInterval(maybeDone, 100); | ||
| }; | ||
| return isKilled = function(pid) { | ||
| var e; | ||
| try { | ||
| process.kill(pid, 0); | ||
| } catch (error) { | ||
| e = error; | ||
| return true; | ||
| } | ||
| return false; | ||
| }; | ||
| } | ||
| }, | ||
| status: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var configPath; | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return lock.find('client', configPath, function(err, exists, piddata) { | ||
| var conn; | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'lock', | ||
| error: err | ||
| }); | ||
| } | ||
| if (!exists) { | ||
| return fail({ | ||
| type: 'exists' | ||
| }); | ||
| } | ||
| conn = new net.Socket(); | ||
| conn.setEncoding('utf8'); | ||
| conn.on('data', function(data) { | ||
| var obj; | ||
| obj = JSON.parse(data); | ||
| return succ({ | ||
| pid: piddata.pid, | ||
| status: obj | ||
| }); | ||
| }); | ||
| return conn.connect(piddata.listen, 'localhost', function() { | ||
| return conn.end('STATUS\x03'); | ||
| }); | ||
| }); | ||
| } | ||
| }, | ||
| sping: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var configPath; | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return lock.find('client', configPath, function(err, exists, piddata) { | ||
| var conn; | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'lock', | ||
| error: err | ||
| }); | ||
| } | ||
| if (!exists) { | ||
| return fail({ | ||
| type: 'exists' | ||
| }); | ||
| } | ||
| conn = new net.Socket(); | ||
| conn.setEncoding('utf8'); | ||
| conn.on('data', function(data) { | ||
| var obj; | ||
| obj = JSON.parse(data); | ||
| succ(obj); | ||
| return conn.end(); | ||
| }); | ||
| return conn.connect(piddata.listen, 'localhost', function() { | ||
| return conn.write('PING\x03'); | ||
| }); | ||
| }); | ||
| } | ||
| }, | ||
| log: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var configPath; | ||
| console.log("argv", opts.argv[0], opts.argv[1]); | ||
| if (opts.argv.length < 2) { | ||
| return fail({ | ||
| type: 'argv', | ||
| error: "No arguments" | ||
| }); | ||
| } | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return lock.find('client', configPath, function(err, exists, piddata) { | ||
| var conn; | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'lock', | ||
| error: err | ||
| }); | ||
| } | ||
| if (!exists) { | ||
| return fail({ | ||
| type: 'exists' | ||
| }); | ||
| } | ||
| conn = new net.Socket(); | ||
| conn.setEncoding('utf8'); | ||
| conn.on('data', function(data) { | ||
| var obj; | ||
| obj = JSON.parse(data); | ||
| succ(obj); | ||
| return conn.end(); | ||
| }); | ||
| return conn.connect(piddata.listen, 'localhost', function() { | ||
| return conn.write("LOG " + opts.argv[1] + "\x03"); | ||
| }); | ||
| }); | ||
| } | ||
| }, | ||
| check: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var configPath; | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return config.readConfig(configPath, function(err, path, data) { | ||
| var bt; | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'config', | ||
| error: err, | ||
| path: path | ||
| }); | ||
| } | ||
| bt = backtalk.create(); | ||
| bt.on('error', function(err) { | ||
| return fail({ | ||
| type: 'message', | ||
| message: "Unable to check.", | ||
| error: err | ||
| }); | ||
| }); | ||
| bt.on('connected', function() { | ||
| return bt.check(function(err, data) { | ||
| if (err != null) { | ||
| return fail(type('message', { | ||
| error: err | ||
| })); | ||
| } | ||
| succ(data); | ||
| return bt.end(); | ||
| }); | ||
| }); | ||
| return bt.open(data.client); | ||
| }); | ||
| } | ||
| }, | ||
| upgrade: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var configPath; | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return config.readConfig(configPath, function(err, path, piddata) { | ||
| var bt, dcb; | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'config', | ||
| error: err, | ||
| path: path | ||
| }); | ||
| } | ||
| bt = backtalk.create(); | ||
| bt.on('error', function(err) { | ||
| return fail({ | ||
| type: 'message', | ||
| message: "Unable to upgrade.", | ||
| error: err | ||
| }); | ||
| }); | ||
| dcb = function(err, data) { | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'upgrade', | ||
| error: err | ||
| }); | ||
| } | ||
| part({ | ||
| type: 'downloaded', | ||
| data: data | ||
| }); | ||
| part({ | ||
| type: 'phase', | ||
| verb: 'applying' | ||
| }); | ||
| return lock.find('client', configPath, function(err, exists, cdata) { | ||
| var buffer, conn; | ||
| if (err != null) { | ||
| return rimraf(data.path, function(rerr) { | ||
| if (rerr != null) { | ||
| part({ | ||
| type: 'error', | ||
| error: rerr | ||
| }); | ||
| } | ||
| return fail({ | ||
| type: 'lock', | ||
| error: err | ||
| }); | ||
| }); | ||
| } else if (!exists) { | ||
| return rimraf(data.path, function(rerr) { | ||
| if (rerr != null) { | ||
| part({ | ||
| type: 'error', | ||
| error: rerr | ||
| }); | ||
| } | ||
| return fail({ | ||
| type: 'exists' | ||
| }); | ||
| }); | ||
| } else if (data.patch != null) { | ||
| conn = new net.Socket(); | ||
| conn.setEncoding('utf8'); | ||
| buffer = ''; | ||
| conn.on('data', function(data) { | ||
| return buffer += data; | ||
| }); | ||
| conn.on('end', function() { | ||
| var checkgo, obj, totalTime; | ||
| conn.end(); | ||
| part({ | ||
| type: 'applied' | ||
| }); | ||
| part({ | ||
| type: 'phase', | ||
| verb: 'restarting' | ||
| }); | ||
| obj = JSON.parse(buffer); | ||
| rimraf(data.path, function(rerr) { | ||
| if (rerr != null) { | ||
| return part({ | ||
| type: 'error', | ||
| error: rerr | ||
| }); | ||
| } | ||
| }); | ||
| if (!obj.success) { | ||
| return fail({ | ||
| type: 'delegate', | ||
| error: obj.error | ||
| }); | ||
| } else { | ||
| totalTime = 0; | ||
| checkgo = function() { | ||
| return lock.find('client', configPath, function(err, exists, newcdata) { | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'postlock', | ||
| error: err | ||
| }); | ||
| } | ||
| if (exists && newcdata.pid !== cdata.pid) { | ||
| return succ({ | ||
| pid: newcdata.pid | ||
| }); | ||
| } else if (totalTime > 6000) { | ||
| return fail({ | ||
| type: 'postfind' | ||
| }); | ||
| } else { | ||
| totalTime += 200; | ||
| return setTimeout(checkgo, 200); | ||
| } | ||
| }); | ||
| }; | ||
| return setTimeout(checkgo, 200); | ||
| } | ||
| }); | ||
| conn.connect(cdata.listen, 'localhost', function() { | ||
| return conn.write("UPGRADE " + (JSON.stringify(data)) + "\x03"); | ||
| }); | ||
| } else { | ||
| succ(null); | ||
| } | ||
| return bt.end(); | ||
| }); | ||
| }; | ||
| bt.on('connected', function() { | ||
| part({ | ||
| type: 'phase', | ||
| verb: 'downloading' | ||
| }); | ||
| return bt.upgrade(dcb, function(sofar, tot) { | ||
| return part({ | ||
| type: 'downloadStatus', | ||
| sofar: sofar, | ||
| total: tot | ||
| }); | ||
| }); | ||
| }); | ||
| return bt.open(piddata.client); | ||
| }); | ||
| } | ||
| }, | ||
| reload: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var configPath, isReload, waitReload; | ||
| configPath = config.pickConfig(opts.options.config); | ||
| lock.find('client', configPath, function(err, exists, piddata) { | ||
| var conn; | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'lock', | ||
| error: err | ||
| }); | ||
| } | ||
| if (!exists) { | ||
| return fail({ | ||
| type: 'exists' | ||
| }); | ||
| } | ||
| conn = new net.Socket(); | ||
| conn.setEncoding('utf8'); | ||
| conn.on('close', function() { | ||
| return waitReload(function(err, rel) { | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'error', | ||
| error: err | ||
| }); | ||
| } | ||
| if (!rel) { | ||
| return fail({ | ||
| type: 'reload' | ||
| }); | ||
| } | ||
| return succ({}); | ||
| }); | ||
| }); | ||
| return conn.connect(piddata.listen, 'localhost', function() { | ||
| return conn.end('RELOAD\x03'); | ||
| }); | ||
| }); | ||
| waitReload = function(cb) { | ||
| return isReload(function(err, rel) { | ||
| var maybeDone, repeater, total; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if (rel) { | ||
| return cb(null, true); | ||
| } | ||
| maybeDone = function() { | ||
| return isReload(function(err, rel) { | ||
| if (err != null) { | ||
| clearInterval(repeater); | ||
| return cb(err); | ||
| } else if (rel) { | ||
| clearInterval(repeater); | ||
| return cb(null, true); | ||
| } else if (total < 60) { | ||
| return ++total; | ||
| } else { | ||
| clearInterval(repeater); | ||
| return cb(null, false); | ||
| } | ||
| }); | ||
| }; | ||
| repeater = setInterval(maybeDone, 100); | ||
| return total = 0; | ||
| }); | ||
| }; | ||
| return isReload = function(cb) { | ||
| return lock.find('client', configPath, cb); | ||
| }; | ||
| } | ||
| }, | ||
| config: { | ||
| opts: [['t', 'tenant=ARG', 'find tenant config']], | ||
| run: function(opts) { | ||
| return config.readConfig(opts.options.config, function(err, configPath, configData) { | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'config', | ||
| path: configPath, | ||
| error: err | ||
| }); | ||
| } | ||
| if (!opts.options.tenant) { | ||
| return util.verifyConfig(configPath, configData, function(err, isReg) { | ||
| var fingerprint, keydata, privateKey, registration; | ||
| if (isReg) { | ||
| if (err) { | ||
| return fail({ | ||
| type: 'verify', | ||
| error: err | ||
| }); | ||
| } | ||
| registration = configData.client.registration; | ||
| delete configData.client.registration; | ||
| keydata = configData.client.keydata; | ||
| delete configData.client.keydata; | ||
| privateKey = forge.pki.privateKeyFromPem(keydata); | ||
| fingerprint = forge.ssh.getPublicKeyFingerprint(privateKey, { | ||
| encoding: 'hex', | ||
| delimiter: ':' | ||
| }); | ||
| multi([ | ||
| [ | ||
| "Registered with IDs", _.map(registration, function(r) { | ||
| return r.id; | ||
| }) | ||
| ], ["Key fingerprint", fingerprint], ["Verified configuration file at " + configPath + "."], ["\"config\":", JSON.stringify(configData.client, null, 2)] | ||
| ]); | ||
| return succ({}); | ||
| } else { | ||
| multi([["This client isn't registered."], ["Verified configuration file at " + configPath + "."], ["\"config\":", JSON.stringify(configData.client, null, 2)]]); | ||
| return succ({}); | ||
| } | ||
| }); | ||
| } else { | ||
| return config.tenantConfig(opts.options.tenant, configData, function(err, tCfgPath, tCfgData) { | ||
| if (err) { | ||
| return fail({ | ||
| type: 'tenant', | ||
| error: err | ||
| }); | ||
| } | ||
| multi([["Tenant config at " + tCfgPath + "."], ["\"config\":", JSON.stringify(tCfgData, null, 2)]]); | ||
| return succ({}); | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| }, | ||
| lease: { | ||
| opts: [['l', 'host=ARG', 'set the local service host'], ['p', 'port=ARG', 'set the local service port']], | ||
| run: function(opts) { | ||
| var configPath; | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return config.readConfig(configPath, function(err, configPath, configData) { | ||
| var e, ref; | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'config', | ||
| error: err, | ||
| path: configPath | ||
| }); | ||
| } | ||
| if (opts.options.host != null) { | ||
| _.set(configData, 'client.local.host', opts.options.host); | ||
| } | ||
| if (opts.options.port != null) { | ||
| try { | ||
| _.set(configData, 'client.local.port', parseInt(opts.options.port)); | ||
| } catch (error) { | ||
| e = error; | ||
| } | ||
| } | ||
| return config.lease((ref = configData.client) != null ? ref : {}, function(err, leasePath) { | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'lease', | ||
| error: err, | ||
| path: leasePath | ||
| }); | ||
| } | ||
| return succ({ | ||
| path: leasePath | ||
| }); | ||
| }); | ||
| }); | ||
| } | ||
| }, | ||
| register: { | ||
| opts: [['t', 'tenant=ARG', 'register by tenant']], | ||
| run: function(opts) { | ||
| var configPath; | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return config.readConfig(configPath, function(err, configPath, configData) { | ||
| var bt; | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'config', | ||
| error: err, | ||
| path: configPath | ||
| }); | ||
| } | ||
| bt = backtalk.create(); | ||
| bt.on('error', function(err) { | ||
| return fail({ | ||
| type: 'message', | ||
| message: "Unable to register.", | ||
| error: err | ||
| }); | ||
| }); | ||
| bt.on('connected', function() { | ||
| return bt.register(opts.options.tenant); | ||
| }); | ||
| bt.on('registered', function(regPath) { | ||
| succ({ | ||
| path: regPath | ||
| }); | ||
| return bt.end(); | ||
| }); | ||
| bt.on('keygen start', function() { | ||
| return part("Generating key (this may take a few minutes) ..."); | ||
| }); | ||
| bt.on('keygen end', function() { | ||
| return part("Key generated."); | ||
| }); | ||
| return bt.open(configData.client); | ||
| }); | ||
| } | ||
| }, | ||
| unregister: { | ||
| opts: [['r', 'regid=ARG', 'unregister by regid'], ['t', 'tenant=ARG', 'unregister by tenant'], ['a', 'all', 'unregister all']], | ||
| run: unregisterRun = function(opts) { | ||
| var configPath, optsuse; | ||
| optsuse = 0; | ||
| if (opts.options.regid) { | ||
| optsuse += 1; | ||
| } | ||
| if (opts.options.tenant) { | ||
| optsuse += 1; | ||
| } | ||
| if (opts.options.all) { | ||
| optsuse += 1; | ||
| } | ||
| if (optsuse !== 1) { | ||
| return fail({ | ||
| type: 'argv', | ||
| error: "Must use exactly one of -a -r -t." | ||
| }); | ||
| } | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return config.readConfig(configPath, function(err, configPath, data) { | ||
| var bt, tenPath; | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'config', | ||
| error: err, | ||
| path: configPath | ||
| }); | ||
| } | ||
| if (opts.options.regid) { | ||
| bt = backtalk.create(); | ||
| bt.on('error', function(err) { | ||
| return fail({ | ||
| type: 'message', | ||
| message: 'Unable to unregister.', | ||
| error: err | ||
| }); | ||
| }); | ||
| bt.on('connected', function() { | ||
| return bt.unregister(opts.options.regid); | ||
| }); | ||
| bt.on('unregistered', function(regPath) { | ||
| succ({ | ||
| path: regPath | ||
| }); | ||
| return bt.end(); | ||
| }); | ||
| return bt.open(data.client); | ||
| } else if (opts.options.tenant) { | ||
| tenPath = path.join(path.dirname(configPath), 'tenants', opts.options.tenant); | ||
| return fs.exists(tenPath, function(exists) { | ||
| if (!exists) { | ||
| return fail({ | ||
| type: 'unregister', | ||
| error: "No such tenant " + tenPath + ".", | ||
| path: tenPath | ||
| }); | ||
| } else { | ||
| return fs.exists(path.join(tenPath, 'registration'), function(exists) { | ||
| if (exists) { | ||
| return fs.readFile(path.join(tenPath, 'registration'), { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| var e, obj, opts2; | ||
| try { | ||
| obj = JSON.parse(data.trim()); | ||
| } catch (error) { | ||
| e = error; | ||
| return fail({ | ||
| type: 'unregister', | ||
| error: e | ||
| }); | ||
| } | ||
| opts2 = _.cloneDeep(opts); | ||
| delete opts2.options.tenant; | ||
| opts2.options.regid = obj.id; | ||
| return unregisterRun(opts2); | ||
| }); | ||
| } else { | ||
| return fail({ | ||
| type: 'unregister', | ||
| error: "Tenant " + tenPath + " is not registered.", | ||
| path: tenPath | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| } else if (opts.options.all) { | ||
| return config.registeredTenants(data, function(err, tenPath, tens) { | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'error', | ||
| error: err | ||
| }); | ||
| } | ||
| return async.mapSeries(tens, function(ten, cb) { | ||
| var em2, opts2; | ||
| em2 = new events.EventEmitter(); | ||
| em2.on('success', function(obj) { | ||
| return cb(null, obj.path); | ||
| }); | ||
| em2.on('failure', function(obj) { | ||
| return cb(obj); | ||
| }); | ||
| em2.on('multi', function(m) { | ||
| return multi(m); | ||
| }); | ||
| em2.on('part', function() { | ||
| var p; | ||
| p = 1 <= arguments.length ? slice.call(arguments, 0) : []; | ||
| return part.apply(null, p); | ||
| }); | ||
| opts2 = _.cloneDeep(opts); | ||
| delete opts2.options.all; | ||
| opts2.options.tenant = ten; | ||
| return commands(em2).unregister.run(opts2); | ||
| }, function(err, paths) { | ||
| if (err != null) { | ||
| return fail(err); | ||
| } | ||
| return succ({ | ||
| paths: paths | ||
| }); | ||
| }); | ||
| }); | ||
| } else { | ||
| return fail({ | ||
| type: 'argv', | ||
| error: "Must use exactly one of -a -r -t." | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| }, | ||
| evict: { | ||
| opts: [['r', 'regid=ARG', 'evict by regid'], ['t', 'tenant=ARG', 'evict by tenant']], | ||
| run: evictRun = function(opts) { | ||
| var configPath; | ||
| if (!opts.options.regid && !opts.options.tenant) { | ||
| return fail({ | ||
| type: 'argv', | ||
| error: "No target specified (use '-r' or '-t')." | ||
| }); | ||
| } | ||
| if (opts.options.regid && opts.options.tenant) { | ||
| return fail({ | ||
| type: 'argv', | ||
| error: "Can't use '-r' and '-t' together." | ||
| }); | ||
| } | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return config.readConfig(configPath, function(err, configPath, data) { | ||
| var bt, tenPath; | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'config', | ||
| error: err, | ||
| path: configPath | ||
| }); | ||
| } | ||
| if (opts.options.regid) { | ||
| bt = backtalk.create(); | ||
| bt.on('error', function(err) { | ||
| return fail({ | ||
| type: 'message', | ||
| message: 'Unable to evict.', | ||
| error: err | ||
| }); | ||
| }); | ||
| bt.on('connected', function() { | ||
| return bt.unregister(opts.options.regid); | ||
| }); | ||
| bt.on('unregistered', function(regPath) { | ||
| var tenPath; | ||
| tenPath = path.dirname(regPath); | ||
| return rimraf(tenPath, function(err) { | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'evict', | ||
| error: err, | ||
| path: tenPath | ||
| }); | ||
| } | ||
| succ({ | ||
| path: tenPath | ||
| }); | ||
| return bt.end(); | ||
| }); | ||
| }); | ||
| return bt.open(data.client); | ||
| } else if (opts.options.tenant) { | ||
| tenPath = path.join(path.dirname(configPath), 'tenants', opts.options.tenant); | ||
| return fs.exists(tenPath, function(exists) { | ||
| if (!exists) { | ||
| return fail({ | ||
| type: 'evict', | ||
| error: "No such tenant " + tenPath + ".", | ||
| path: tenPath | ||
| }); | ||
| } else { | ||
| return fs.exists(path.join(tenPath, 'registration'), function(exists) { | ||
| if (exists) { | ||
| return fs.readFile(path.join(tenPath, 'registration'), { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| var e, obj; | ||
| try { | ||
| obj = JSON.parse(data.trim()); | ||
| } catch (error) { | ||
| e = error; | ||
| return fail({ | ||
| type: 'evict', | ||
| error: e | ||
| }); | ||
| } | ||
| return evictRun({ | ||
| options: { | ||
| regid: obj.id | ||
| } | ||
| }); | ||
| }); | ||
| } else { | ||
| return rimraf(tenPath, function(err) { | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'message', | ||
| error: "Unable to evict " + tenPath + ": " + err | ||
| }); | ||
| } | ||
| return succ({ | ||
| path: tenPath | ||
| }); | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| }, | ||
| tenants: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var configPath; | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return config.readConfig(configPath, function(err, configPath, configData) { | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'config', | ||
| error: err, | ||
| path: configPath | ||
| }); | ||
| } | ||
| return config.allTenants(configData, function(err, tenDir, tens) { | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'tenants', | ||
| error: err | ||
| }); | ||
| } | ||
| return async.each(tens, function(ten, cb) { | ||
| return config.tenantConfig(ten.name, configData, function(err, tCfgPath, tCfgData) { | ||
| ten.config = tCfgData; | ||
| return cb(null); | ||
| }); | ||
| }, function(err) { | ||
| return succ({ | ||
| tenants: tens | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| } | ||
| }, | ||
| ping: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var configPath; | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return lock.find('client', configPath, function(err, exists, data) { | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'lock', | ||
| error: err | ||
| }); | ||
| } | ||
| if (exists) { | ||
| return fail({ | ||
| type: 'exists', | ||
| pid: data.pid | ||
| }); | ||
| } | ||
| return config.readConfig(configPath, function(err, configPath, configData) { | ||
| if (err != null) { | ||
| return fail({ | ||
| type: 'config', | ||
| error: err, | ||
| path: configPath | ||
| }); | ||
| } | ||
| return util.verifyConfig(configPath, configData, function(err, isReg) { | ||
| var bt; | ||
| if (err) { | ||
| return fail({ | ||
| type: 'verify', | ||
| error: err | ||
| }); | ||
| } | ||
| bt = backtalk.create(); | ||
| bt.on('error', function(err) { | ||
| return fail({ | ||
| type: 'message', | ||
| message: 'Unable to ping.', | ||
| error: err | ||
| }); | ||
| }); | ||
| bt.on('connected', function() { | ||
| return part("Connected."); | ||
| }); | ||
| bt.on('handshake', function() { | ||
| succ({}); | ||
| return bt.end(); | ||
| }); | ||
| return bt.connect({ | ||
| config: configData.client | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| } | ||
| }, | ||
| version: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var configPath; | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return config.readConfig(configPath, function(err, configPath, configData) { | ||
| if (err != null) { | ||
| return fail({ | ||
| message: 'config err', | ||
| error: err | ||
| }); | ||
| } | ||
| return succ(configData.runtime || config.version()); | ||
| }); | ||
| } | ||
| } | ||
| }; | ||
| }; | ||
| }).call(this); |
| (function() { | ||
| var applyDiff, applyDiffChunk, applyMain, async, diff, fs, mkdirp, path, resolvePath, targz, unlink, util; | ||
| async = require('async'); | ||
| fs = require('fs'); | ||
| path = require('path'); | ||
| targz = require('tar'); | ||
| mkdirp = require('mkdirp'); | ||
| diff = require('diff'); | ||
| util = require('./util'); | ||
| exports.makeDest = function(config, cb) { | ||
| var upath; | ||
| if (config.configPath == null) { | ||
| return cb(new Error("No configPath listed")); | ||
| } | ||
| upath = path.join(path.dirname(config.configPath), 'upgrades', util.guid()); | ||
| return mkdirp(upath, function(err) { | ||
| if (err != null) { | ||
| return cb(new Error("Couldn't make upgrades dir: " + err)); | ||
| } | ||
| return cb(null, { | ||
| path: function() { | ||
| return upath; | ||
| }, | ||
| outstream: function() { | ||
| return targz().createWriteStream(upath); | ||
| } | ||
| }); | ||
| }); | ||
| }; | ||
| exports.applyPatch = function(patch, ppath, configPath, cb) { | ||
| var main; | ||
| if (!patch.main) { | ||
| return fs.readdir(ppath, function(err, files) { | ||
| var mains; | ||
| mains = _.filter(files, function(f) { | ||
| return f.startsWith('main.'); | ||
| }); | ||
| if (mains.length !== 1) { | ||
| return cb(new Error("Couldn't choose main from " + mains + ".")); | ||
| } | ||
| return applyMain(patch, ppath, configPath, path.join(ppath, mains[0]), cb); | ||
| }); | ||
| } else { | ||
| main = path.join(ppath, patch.main); | ||
| return fs.exists(main, function(exists) { | ||
| if (exists) { | ||
| return applyMain(patch, ppath, main, configPath, cb); | ||
| } else { | ||
| return cb(new Error("Main file " + main + " doesn't exist.")); | ||
| } | ||
| }); | ||
| } | ||
| }; | ||
| exports.applyMain = applyMain = function(patch, ppath, main, configPath, cb) { | ||
| switch (path.extname(main)) { | ||
| case '.js': | ||
| return applyJs(patch, ppath, main, configPath, cb); | ||
| case '.exe': | ||
| return applyExe(patch, ppath, main, configPath, cb); | ||
| case '.diff': | ||
| case '.patch': | ||
| return applyDiff(patch, ppath, main, configPath, cb); | ||
| default: | ||
| return cb(new Error("Unknown main type " + main + ".")); | ||
| } | ||
| }; | ||
| exports.applyDiff = applyDiff = function(patch, ppath, main, configPath, cb) { | ||
| var chunk, chunkStart, ended, fin, readLoop, reading; | ||
| fin = fs.createReadStream(main, { | ||
| encoding: 'utf8' | ||
| }); | ||
| chunk = ''; | ||
| chunkStart = 0; | ||
| reading = false; | ||
| ended = false; | ||
| readLoop = function(err) { | ||
| var m, newchunk; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| reading = true; | ||
| if ((m = chunk.match(/\n[^ \-\+\@\\]/)) != null) { | ||
| newchunk = chunk.substr(0, m.index); | ||
| chunkStart += m.index; | ||
| chunk = chunk.substr(m.index + 1); | ||
| return applyDiffChunk(newchunk, configPath, readLoop); | ||
| } else if (ended) { | ||
| newchunk = chunk; | ||
| chunkStart += chunk.length; | ||
| chunk = ''; | ||
| return applyDiffChunk(newchunk, configPath, cb); | ||
| } else { | ||
| return reading = false; | ||
| } | ||
| }; | ||
| return fin.on('readable', function() { | ||
| var data; | ||
| data = fin.read(); | ||
| if (data != null) { | ||
| chunk += data; | ||
| } else { | ||
| ended = true; | ||
| } | ||
| if (!reading) { | ||
| return readLoop(); | ||
| } | ||
| }); | ||
| }; | ||
| applyDiffChunk = function(diffChunk, configPath, cb) { | ||
| var dfile, dm, n, sfile, sm, withOrig; | ||
| n = diffChunk.indexOf('\n'); | ||
| if (n >= 0) { | ||
| diffChunk = diffChunk.substr(n + 1); | ||
| } | ||
| sm = /^--- ([^\n\r\t]+).*/m.exec(diffChunk); | ||
| if (sm != null ? sm[1] : void 0) { | ||
| sfile = resolvePath(sm[1], configPath); | ||
| } else { | ||
| return cb(new Error("Unable to find source file in " + diffChunk)); | ||
| } | ||
| dm = /^\+\+\+ ([^\n\r\t]+).*/m.exec(diffChunk); | ||
| if (dm != null ? dm[1] : void 0) { | ||
| dfile = resolvePath(dm[1], configPath); | ||
| } else { | ||
| return cb(new Error("Unable to find dest file")); | ||
| } | ||
| withOrig = function(err, orig) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return mkdirp(path.dirname(dfile), function(err) { | ||
| var patched; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| patched = diff.applyPatch(orig, diffChunk); | ||
| if (dfile === '/dev/null' && patched.length === 0) { | ||
| return unlink(sfile, cb); | ||
| } else if (patched.length === 0) { | ||
| return unlink(dfile, cb); | ||
| } else { | ||
| return fs.writeFile(dfile, patched, { | ||
| encoding: 'utf8' | ||
| }, cb); | ||
| } | ||
| }); | ||
| }; | ||
| return fs.exists(sfile, function(exists) { | ||
| if (exists) { | ||
| return fs.readFile(sfile, { | ||
| encoding: 'utf8' | ||
| }, withOrig); | ||
| } else { | ||
| return withOrig(null, ''); | ||
| } | ||
| }); | ||
| }; | ||
| unlink = function(fname, cb) { | ||
| return fs.exists(fname, function(exists) { | ||
| if (exists) { | ||
| return fs.unlink(fname, cb); | ||
| } else { | ||
| return cb(null); | ||
| } | ||
| }); | ||
| }; | ||
| resolvePath = function(sfile, configPath) { | ||
| if (sfile.startsWith('a/') || sfile.startsWith('b/')) { | ||
| sfile = sfile.substr(2); | ||
| } | ||
| if (!path.parse(sfile).root) { | ||
| sfile = path.join(path.dirname(configPath), sfile); | ||
| } | ||
| return sfile; | ||
| }; | ||
| }).call(this); |
| #!/usr/bin/env node | ||
| (function() { | ||
| var _, commands, events, getopt, main, outputs, say, stdEmit, warn, wordwrap, | ||
| slice = [].slice; | ||
| _ = require('lodash'); | ||
| getopt = require('node-getopt'); | ||
| events = require('events'); | ||
| wordwrap = require('wordwrap'); | ||
| say = console.log; | ||
| warn = console.warn; | ||
| commands = require('./manage').commands; | ||
| main = function() { | ||
| var cmd, opt, opts, optsrc, ref, ref1, task, usage; | ||
| usage = function() { | ||
| say("Usage: petit-client <command> [OPTIONS]"); | ||
| say("Known commands are:"); | ||
| say(wordwrap(2, 72)(_.sortBy(_.keys(commands())).join(', '))); | ||
| return process.exit(1); | ||
| }; | ||
| if (process.argv.length < 3 || ((ref = process.argv[2]) === '-h' || ref === '--help')) { | ||
| usage(); | ||
| } | ||
| cmd = process.argv[2]; | ||
| task = commands(stdEmit(cmd))[cmd]; | ||
| if (task == null) { | ||
| say("Unknown command \"" + process.argv[2] + "\""); | ||
| usage(); | ||
| } | ||
| optsrc = (ref1 = task.opts) != null ? ref1 : []; | ||
| optsrc.push.apply(optsrc, [['c', 'config=ARG', 'use config file'], ['v', 'verbose', 'print debug output'], ['h', 'help', 'display this help']]); | ||
| opt = getopt.create(optsrc); | ||
| opt.setHelp("Usage: petit-client " + process.argv[2] + " [OPTION]\n\n[[OPTIONS]]"); | ||
| opts = opt.bindHelp().parseSystem(); | ||
| return task.run(opts); | ||
| }; | ||
| stdEmit = function(cmd) { | ||
| var emitter, output; | ||
| emitter = new events.EventEmitter(); | ||
| output = outputs[cmd]; | ||
| if (typeof output === 'function') { | ||
| output = output(); | ||
| } | ||
| emitter.on('success', function(obj) { | ||
| var out; | ||
| if ((out = output != null ? output.succ : void 0) != null) { | ||
| return out(obj); | ||
| } else { | ||
| return say("Uncategorized " + cmd + " success: " + (JSON.stringify(obj)) + "."); | ||
| } | ||
| }); | ||
| emitter.on('failure', function(obj) { | ||
| var out; | ||
| if ((out = output != null ? output.fail : void 0) != null) { | ||
| out(obj); | ||
| } else { | ||
| warn("Uncategorized " + cmd + " failure: " + (JSON.stringify(obj)) + "."); | ||
| } | ||
| return process.exit(101); | ||
| }); | ||
| emitter.on('multi', function(multi) { | ||
| var i, len, m, results; | ||
| results = []; | ||
| for (i = 0, len = multi.length; i < len; i++) { | ||
| m = multi[i]; | ||
| results.push(say.apply(null, m)); | ||
| } | ||
| return results; | ||
| }); | ||
| emitter.on('part', function() { | ||
| var out, part; | ||
| part = 1 <= arguments.length ? slice.call(arguments, 0) : []; | ||
| if ((out = output != null ? output.part : void 0) != null) { | ||
| return out.apply(null, part); | ||
| } else { | ||
| return say.apply(null, part); | ||
| } | ||
| }); | ||
| return emitter; | ||
| }; | ||
| outputs = { | ||
| init: { | ||
| succ: function(obj) { | ||
| return say("Created configuration file \"" + obj.path + "\"."); | ||
| }, | ||
| fail: function(obj) { | ||
| switch (obj.type) { | ||
| case 'config': | ||
| return warn("Couldn't create file \"" + obj.path + "\".", obj.error); | ||
| case 'exists': | ||
| return warn("Configuration file \"" + obj.path + "\" exists."); | ||
| default: | ||
| return warn("Unknown init error", obj.message, obj.error); | ||
| } | ||
| } | ||
| }, | ||
| start: { | ||
| succ: function(obj) { | ||
| return say("Client started on pid " + obj.pid + "."); | ||
| }, | ||
| fail: function(obj) { | ||
| switch (obj.type) { | ||
| case 'config': | ||
| return warn("Couldn't read config \"" + obj.path + "\".", obj.error); | ||
| case 'lock': | ||
| return warn("Couldn't lock client", obj.error); | ||
| case 'exists': | ||
| return warn("Client is already running on pid " + obj.pid); | ||
| case 'message': | ||
| return warn(obj.message, obj.error); | ||
| case 'postlock': | ||
| return warn("Couldn't lock after starting", obj.error); | ||
| case 'postfind': | ||
| return warn("Couldn't start client."); | ||
| default: | ||
| return warn("Unknown start error", obj.message, obj.error); | ||
| } | ||
| } | ||
| }, | ||
| stop: { | ||
| succ: function(obj) { | ||
| if (obj.sigint) { | ||
| return say("Client is stopped with SIGINT."); | ||
| } else { | ||
| return say("Client stopped."); | ||
| } | ||
| }, | ||
| fail: function(obj) { | ||
| switch (obj.type) { | ||
| case 'lock': | ||
| return warn("Couldn't lock client", obj.error); | ||
| case 'exists': | ||
| return warn("Client is not running."); | ||
| case 'unstop': | ||
| return warn("Unable to stop process on pid " + obj.pid + "."); | ||
| default: | ||
| return warn("Unknown stop error", obj.message, obj.error); | ||
| } | ||
| } | ||
| }, | ||
| status: { | ||
| succ: function(obj) { | ||
| return say(("Client has status on pid " + obj.pid + "\n") + JSON.stringify(obj.status, null, 2)); | ||
| }, | ||
| fail: function(obj) { | ||
| switch (obj.type) { | ||
| case 'lock': | ||
| return warn("Couldn't lock client", obj.error); | ||
| case 'exists': | ||
| return warn("Client isn't running."); | ||
| default: | ||
| return warn("Unknown status error", obj.message, obj.error); | ||
| } | ||
| } | ||
| }, | ||
| sping: { | ||
| succ: function(obj) { | ||
| return say("Client ping: " + (JSON.stringify(obj))); | ||
| }, | ||
| fail: function(obj) { | ||
| switch (obj.type) { | ||
| case 'lock': | ||
| return warn("Couldn't lock client", obj.error); | ||
| case 'exists': | ||
| return warn("Client isn't running."); | ||
| default: | ||
| return warn("Unknown ping error", obj.message, obj.error); | ||
| } | ||
| } | ||
| }, | ||
| log: { | ||
| succ: function(obj) { | ||
| return say("Logging level set to \"" + obj.level + "\"."); | ||
| }, | ||
| fail: function(obj) { | ||
| switch (obj.type) { | ||
| case 'argv': | ||
| return warn("Can't find log level to set to."); | ||
| case 'lock': | ||
| return warn("Couldn't find running client."); | ||
| case 'exists': | ||
| return warn("Client isn't running."); | ||
| case 'log': | ||
| return warn("Couldn't changing logging", obj.error); | ||
| default: | ||
| return warn("Unknown logset error", obj.message, obj.error); | ||
| } | ||
| } | ||
| }, | ||
| check: { | ||
| succ: function(obj) { | ||
| return say("Client check: " + (JSON.stringify(obj))); | ||
| }, | ||
| fail: function(obj) { | ||
| switch (obj.type) { | ||
| case 'config': | ||
| return warn("Couldn't read config", obj.error); | ||
| case 'message': | ||
| return warn("Message error.", obj.error); | ||
| default: | ||
| return warn("Unknown check error", obj.message, obj.error); | ||
| } | ||
| } | ||
| }, | ||
| upgrade: function() { | ||
| var lastDed; | ||
| lastDed = 0; | ||
| return { | ||
| succ: function(obj) { | ||
| return say("Client upgraded and restarted on pid " + obj.pid + "."); | ||
| }, | ||
| fail: function(obj) { | ||
| switch (obj.type) { | ||
| case 'config': | ||
| return warn("Couldn't read config", obj.error); | ||
| case 'message': | ||
| return warn("Message error.", obj.error); | ||
| case 'upgrade': | ||
| return warn("Upgrade error.", obj.error); | ||
| case 'exists': | ||
| return warn("Client isn't running."); | ||
| case 'error': | ||
| return warn("Couln't upgrade: " + obj.error); | ||
| case 'lock': | ||
| return warn("Couldn't lock client.", obj.error); | ||
| case 'postlock': | ||
| return warn("Couldn't lock client again.", obj.error); | ||
| case 'postfind': | ||
| return warn("Couldn't restart after upgrade."); | ||
| case 'delegate': | ||
| return warn("Failed delegated upgrade.", obj.error); | ||
| case 'unstop': | ||
| return warn("Unable to stop process on pid " + obj.pid + "."); | ||
| default: | ||
| return warn("Unknown upgrade error", obj.type, obj.message, obj.error); | ||
| } | ||
| }, | ||
| part: function(part) { | ||
| var results; | ||
| switch (part.type) { | ||
| case 'downloadStatus': | ||
| if (part.sofar >= lastDed + 10000 || part.sofar === part.total) { | ||
| say("received " + part.sofar + " / " + part.total + " bytes"); | ||
| } | ||
| results = []; | ||
| while (part.sofar >= lastDed + 10000) { | ||
| lastDed = Math.min(lastDed + 10000, part.total); | ||
| if (lastDed >= part.total) { | ||
| break; | ||
| } else { | ||
| results.push(void 0); | ||
| } | ||
| } | ||
| return results; | ||
| break; | ||
| case 'downloaded': | ||
| return say('Download complete.', JSON.stringify(part.data)); | ||
| case 'applied': | ||
| return say('Applied.'); | ||
| case 'phase': | ||
| return say("Now " + part.verb + "..."); | ||
| case 'error': | ||
| return say("Error " + part.error); | ||
| default: | ||
| return say("Unknown upgrade partial data", part.type); | ||
| } | ||
| } | ||
| }; | ||
| }, | ||
| reload: { | ||
| succ: function(obj) { | ||
| return say("Client is reloaded."); | ||
| }, | ||
| fail: function(obj) { | ||
| switch (obj.type) { | ||
| case 'lock': | ||
| return warn("Couldn't lock client", obj.error); | ||
| case 'exists': | ||
| return warn("Client isn't running."); | ||
| case 'error': | ||
| return warn("Error while reloading.", obj.error); | ||
| case 'reload': | ||
| return warn("Couldn't reload."); | ||
| default: | ||
| return warn("Unknown reload error", obj.message, obj.error); | ||
| } | ||
| } | ||
| }, | ||
| config: { | ||
| succ: function(obj) {}, | ||
| fail: function(obj) { | ||
| switch (obj.type) { | ||
| case 'config': | ||
| return warn("Couldn't read config \"" + obj.path + "\".", obj.error); | ||
| case 'verify': | ||
| return warn("Couldn't verify configuration.", obj.error); | ||
| case 'tenant': | ||
| return warn("Couldn't get tenant.", obj.error); | ||
| default: | ||
| return warn("Unknown config error", obj.message, obj.error); | ||
| } | ||
| } | ||
| }, | ||
| register: { | ||
| succ: function(obj) { | ||
| return say("Registered to \"" + obj.path + "\"."); | ||
| }, | ||
| fail: function(obj) { | ||
| switch (obj.type) { | ||
| case 'config': | ||
| return warn("Couldn't read config \"" + obj.path + "\".", obj.error); | ||
| case 'exists': | ||
| return warn("Client is already registered."); | ||
| case 'message': | ||
| return warn(obj.message, obj.error); | ||
| default: | ||
| return warn("Unknown register error", obj.message, obj.error); | ||
| } | ||
| } | ||
| }, | ||
| lease: { | ||
| succ: function(obj) { | ||
| return say("New lease at \"" + obj.path + "\"."); | ||
| }, | ||
| fail: function(obj) { | ||
| switch (obj.type) { | ||
| case 'config': | ||
| return warn("Couldn't read config \"" + obj.path + "\".", obj.error); | ||
| case 'lease': | ||
| return warn("Couldn't lease to " + obj.path); | ||
| default: | ||
| return warn("Unknown lease error", obj.message, obj.error); | ||
| } | ||
| } | ||
| }, | ||
| unregister: { | ||
| succ: function(obj) { | ||
| if (obj.path != null) { | ||
| return say("Unregistered tenant " + obj.path + "."); | ||
| } else { | ||
| return say("Unregistered all tenants " + obj.paths + "."); | ||
| } | ||
| }, | ||
| fail: function(obj) { | ||
| switch (obj.type) { | ||
| case 'argv': | ||
| return warn("" + obj.error); | ||
| case 'config': | ||
| return warn("Couldn't read config \"" + obj.path + "\".", obj.error); | ||
| case 'exists': | ||
| return warn("Client is not registered."); | ||
| case 'message': | ||
| return warn(obj.message, obj.error); | ||
| case 'unregister': | ||
| return warn("Couldn't unregister tenant " + obj.path, obj.error); | ||
| default: | ||
| return warn("Unknown unregister error", obj.type, obj.message, obj.error); | ||
| } | ||
| } | ||
| }, | ||
| evict: { | ||
| succ: function(obj) { | ||
| return say("Evicted tenant at " + obj.path + "."); | ||
| }, | ||
| fail: function(obj) { | ||
| switch (obj.type) { | ||
| case 'argv': | ||
| return warn("" + obj.error); | ||
| case 'config': | ||
| return warn("Couldn't read config \"" + obj.path + "\".", obj.error); | ||
| case 'exists': | ||
| return warn("Client is not registered."); | ||
| case 'message': | ||
| return warn(obj.message, obj.error); | ||
| case 'evict': | ||
| return warn("Couldn't evict tenant " + obj.path, obj.error); | ||
| case 'registered': | ||
| return warn("Already registered: " + obj.error); | ||
| default: | ||
| return warn("Unknown unregister error", obj.type, obj.message, obj.error); | ||
| } | ||
| } | ||
| }, | ||
| tenants: { | ||
| succ: function(obj) { | ||
| var hostport, i, len, ref, ref1, ref2, results, ten; | ||
| if (_.isEmpty(obj.tenants)) { | ||
| return say("No tenants found."); | ||
| } else { | ||
| say("Found tenants:"); | ||
| ref = obj.tenants; | ||
| results = []; | ||
| for (i = 0, len = ref.length; i < len; i++) { | ||
| ten = ref[i]; | ||
| hostport = ((ref1 = ten.config) != null ? (ref2 = ref1.local) != null ? ref2.host : void 0 : void 0) ? " " + ten.config.local.host + ":" + ten.config.local.port : ''; | ||
| if (ten.registered) { | ||
| results.push(say(" - " + ten.name + " " + ten.registeredId + hostport)); | ||
| } else { | ||
| results.push(say(" " + ten.name + hostport)); | ||
| } | ||
| } | ||
| return results; | ||
| } | ||
| }, | ||
| fail: function(obj) { | ||
| switch (obj.type) { | ||
| case 'config': | ||
| return warn("Couldn't read config \"" + obj.path + "\".", obj.error); | ||
| case 'tenants': | ||
| return warn("Couldn't find tenants " + obj.error); | ||
| default: | ||
| return warn("Unknown tenants error", obj.type, obj.message, obj.error); | ||
| } | ||
| } | ||
| }, | ||
| ping: { | ||
| succ: function(obj) { | ||
| return say("Ping success."); | ||
| }, | ||
| fail: function(obj) { | ||
| switch (obj.type) { | ||
| case 'lock': | ||
| return warn("Couldn't lock client", obj.error); | ||
| case 'exists': | ||
| return warn("Client is running."); | ||
| case 'config': | ||
| return warn("Couldn't read config \"" + obj.path + "\".", obj.error); | ||
| case 'verify': | ||
| return warn("Couldn't verify configuration.", obj.error); | ||
| case 'message': | ||
| return warn(obj.message, obj.error); | ||
| default: | ||
| return warn("Unknown ping error", obj.message, obj.error); | ||
| } | ||
| } | ||
| }, | ||
| version: { | ||
| succ: function(obj) { | ||
| var name, prov; | ||
| name = obj.name || "Ground2Cloud Client"; | ||
| prov = obj.provider || "Cloud Elements <cloud-elements.com>"; | ||
| say(name + " version " + obj.display); | ||
| return say("by " + prov); | ||
| }, | ||
| fail: function(obj) { | ||
| return warn("Unknown version error", obj.message, obj.error); | ||
| } | ||
| } | ||
| }; | ||
| main(); | ||
| }).call(this); |
| #!/usr/bin/env node | ||
| (function() { | ||
| var SEVERE, _, async, cfgPath, cl, client, config, daemon, errors, events, filelock, fs, getopt, handle, handleConfig, healer, heartbeat, heartintv, http, https, iceptPath, level, load, loading, lock, loopbeat, loopintv, main, manage, opt, output, patches, path, registerRestoreTenants, restapi, sendLoop, service, startHeartbeat, startRest, stopHeartbeat, tunnel, unload, unloadNoUnlock, unlock, url, util, winston, | ||
| slice = [].slice; | ||
| _ = require('lodash'); | ||
| async = require('async'); | ||
| client = require('./client'); | ||
| config = require('../config'); | ||
| errors = require('../errors'); | ||
| events = require('events'); | ||
| fs = require('fs'); | ||
| getopt = require('node-getopt'); | ||
| healer = require('./healer'); | ||
| http = require('http'); | ||
| https = require('https'); | ||
| lock = require('../lock'); | ||
| manage = require('./manage'); | ||
| patches = require('./patches'); | ||
| path = require('path'); | ||
| restapi = require('./restapi'); | ||
| tunnel = require('./tunnel'); | ||
| url = require('url'); | ||
| util = require('./util'); | ||
| winston = require('winston'); | ||
| SEVERE = errors.SEVERE; | ||
| opt = getopt.create([['v', 'verbose', 'print debug output'], ['c', 'config=ARG', 'set config file'], ['s', 'service', 'this is a service run'], ['h', 'help', 'display this help']]).bindHelp().parseSystem(); | ||
| cfgPath = config.pickConfig(opt.options.config); | ||
| level = opt.options.verbose ? 'verbose' : 'normal'; | ||
| service = opt.options.service; | ||
| output = null; | ||
| heartbeat = null; | ||
| heartintv = 120000; | ||
| loopbeat = null; | ||
| loopintv = 300000; | ||
| iceptPath = tunnel.iceptPath; | ||
| daemon = { | ||
| output: output, | ||
| service: service | ||
| }; | ||
| cl = client.create(daemon); | ||
| filelock = null; | ||
| main = function() { | ||
| handle(cl); | ||
| return load(function() {}); | ||
| }; | ||
| startHeartbeat = function(cb) { | ||
| return stopHeartbeat(function() { | ||
| var thump, whoosh; | ||
| thump = function() { | ||
| return cl.backchannel.ping(function(err, d) { | ||
| if (err != null) { | ||
| return output.warn(err); | ||
| } else { | ||
| return output.log("Heartbeat success: " + (d.type === 'didPing1')); | ||
| } | ||
| }); | ||
| }; | ||
| heartbeat = setInterval(thump, heartintv); | ||
| whoosh = function() { | ||
| return sendLoop(cl.config, function(err, d) { | ||
| if (err != null) { | ||
| return output.warn("Loopback failure: " + err); | ||
| } else { | ||
| return output.log("Loopback success: true for " + (JSON.stringify(d))); | ||
| } | ||
| }); | ||
| }; | ||
| loopbeat = setInterval(whoosh, loopintv); | ||
| return typeof cb === "function" ? cb() : void 0; | ||
| }); | ||
| }; | ||
| stopHeartbeat = function(cb) { | ||
| if (heartbeat != null) { | ||
| clearInterval(heartbeat); | ||
| } | ||
| heartbeat = null; | ||
| if (loopbeat != null) { | ||
| clearInterval(loopbeat); | ||
| } | ||
| loopbeat = null; | ||
| return typeof cb === "function" ? cb() : void 0; | ||
| }; | ||
| sendLoop = function(config, cb) { | ||
| var eps, ref, ref1; | ||
| if ((eps = (ref = config.server) != null ? (ref1 = ref.vitae) != null ? ref1.endpoints : void 0 : void 0) == null) { | ||
| return cb("No server endpoint found for loopback."); | ||
| } | ||
| return async.map(_.values(eps), function(ep, cb) { | ||
| var caPath, certDir, handler, i, len, p, props, ref2; | ||
| props = url.parse(ep); | ||
| _.assign(props, { | ||
| path: iceptPath, | ||
| method: 'GET' | ||
| }); | ||
| ref2 = ['host', 'href', 'pathname', 'search']; | ||
| for (i = 0, len = ref2.length; i < len; i++) { | ||
| p = ref2[i]; | ||
| delete props[p]; | ||
| } | ||
| handler = function(res) { | ||
| if (res.statusCode === 200) { | ||
| return cb(null, ep); | ||
| } else { | ||
| return cb("Loopback code: " + res.statusCode); | ||
| } | ||
| }; | ||
| switch (props.protocol) { | ||
| case 'https:': | ||
| if (props.port == null) { | ||
| props.port = 443; | ||
| } | ||
| certDir = path.join(path.dirname(config.configPath), 'certificates'); | ||
| caPath = path.join(certDir, 'ca.bundle'); | ||
| if (fs.existsSync(caPath)) { | ||
| props.ca = fs.readFileSync(caPath); | ||
| props.agent = new https.Agent(props); | ||
| } | ||
| client = https.request(props, handler); | ||
| break; | ||
| case 'http:': | ||
| if (props.port == null) { | ||
| props.port = 80; | ||
| } | ||
| client = http.request(props, handler); | ||
| break; | ||
| default: | ||
| return cb("Unknown protocol " + props.protocol); | ||
| } | ||
| client.on('error', cb); | ||
| return client.end(); | ||
| }, cb); | ||
| }; | ||
| loading = false; | ||
| daemon.load = load = function(cb) { | ||
| if (loading) { | ||
| return cb("Loading in progress."); | ||
| } | ||
| loading = true; | ||
| output.log('Loading'); | ||
| return config.readConfig(opt.options.config, handleConfig(function() { | ||
| var args; | ||
| args = 1 <= arguments.length ? slice.call(arguments, 0) : []; | ||
| output.log('Load complete.'); | ||
| loading = false; | ||
| return cb.apply(null, args); | ||
| })); | ||
| }; | ||
| daemon.unload = unload = function(cb) { | ||
| if (loading) { | ||
| return cb("Unloading in progress."); | ||
| } | ||
| loading = true; | ||
| output.log('Unloading', filelock); | ||
| if (filelock != null) { | ||
| lock.unlock(filelock); | ||
| } | ||
| return stopHeartbeat(function() { | ||
| output.log('Unload heartbeat stopped.'); | ||
| return cl.end(function() { | ||
| output.log('Unload client ended.'); | ||
| return cl.deafen(function() { | ||
| var args; | ||
| args = 1 <= arguments.length ? slice.call(arguments, 0) : []; | ||
| output.log('Unload client deafened. Complete.'); | ||
| loading = false; | ||
| return cb.apply(null, args); | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| daemon.unloadNoUnlock = unloadNoUnlock = function(cb) { | ||
| if (loading) { | ||
| return cb("Unloading (w/out unlock) in progress."); | ||
| } | ||
| loading = true; | ||
| return stopHeartbeat(function() { | ||
| output.log('Unload heartbeat stopped.'); | ||
| return cl.end(function() { | ||
| output.log('Unload client ended.'); | ||
| return cl.deafen(function() { | ||
| var args; | ||
| args = 1 <= arguments.length ? slice.call(arguments, 0) : []; | ||
| output.log('Unload client deafened. Complete.'); | ||
| loading = false; | ||
| return cb.apply(null, args); | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| daemon.unlock = unlock = function(cb) { | ||
| if (filelock != null) { | ||
| lock.unlock(filelock); | ||
| } | ||
| return cb(null); | ||
| }; | ||
| handleConfig = function(cb) { | ||
| return function(err, configPath, configData) { | ||
| if (err != null) { | ||
| output.prettyConfig(SEVERE, err); | ||
| return cb(err); | ||
| } | ||
| return registerRestoreTenants(configData.client, function(err) { | ||
| var ref; | ||
| if (err != null) { | ||
| output.warn("Couldn't restore some tenants"); | ||
| } | ||
| if (((ref = configData.client) != null ? ref.logLevel : void 0) != null) { | ||
| output.setLevel(configData.client.logLevel); | ||
| } | ||
| return util.verifyConfig(configPath, configData, function(err, isReg) { | ||
| if (err != null) { | ||
| output.prettyConfig(SEVERE, err); | ||
| return cb(err); | ||
| } | ||
| return lock.lock('client', { | ||
| pid: process.pid, | ||
| listen: configData.client.listenPort | ||
| }, configPath, configData, function(err, v) { | ||
| var ref1; | ||
| if (err != null) { | ||
| output.warn(err); | ||
| return cb(err); | ||
| } | ||
| filelock = v; | ||
| if ((ref1 = configData.client) != null) { | ||
| ref1.configPath = configData.configPath; | ||
| } | ||
| if (isReg) { | ||
| return cl.listen(configData.client, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return cl.start(configData.client, cb); | ||
| }); | ||
| } else { | ||
| return cl.listen(configData.client, cb); | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| }; | ||
| registerRestoreTenants = function(cfgData, cb) { | ||
| var savePath; | ||
| savePath = path.join(path.dirname(cfgData.configPath), 'restore'); | ||
| return fs.exists(savePath, function(exists) { | ||
| if (!exists) { | ||
| return cb(null); | ||
| } | ||
| return fs.readFile(savePath, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| var e, tenantList; | ||
| if (err != null) { | ||
| output.warn("Error reading " + savePath + ": proceeding anyways"); | ||
| fs.unlink(savePath); | ||
| return cb(null); | ||
| } | ||
| try { | ||
| tenantList = JSON.parse(data.trim()); | ||
| } catch (error) { | ||
| e = error; | ||
| output.warn("Error parsing " + savePath + ": " + e + ": proceeding anyways"); | ||
| fs.unlink(savePath); | ||
| return cb(null); | ||
| } | ||
| if (!(_.isArray(tenantList))) { | ||
| output.warn("Data in " + savePath + " is not an array"); | ||
| fs.unlink(savePath); | ||
| return cb(null); | ||
| } | ||
| return config.registeredTenants(cfgData, function(err, tenpath, dirs) { | ||
| var newTenantList; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| newTenantList = []; | ||
| return fs.unlink(savePath, function() { | ||
| return async.eachSeries(tenantList, function(tenname, cb) { | ||
| var handler, opts, singleCb, singleton; | ||
| if ((dirs != null) && dirs.indexOf(tenname) !== -1) { | ||
| return cb(null); | ||
| } | ||
| singleton = false; | ||
| singleCb = function(fromTimeout, err) { | ||
| return function() { | ||
| if (singleton) { | ||
| return; | ||
| } | ||
| singleton = true; | ||
| if (fromTimeout) { | ||
| output.log("Restore timed out: proceeding anyways"); | ||
| } | ||
| if ((err != null) || fromTimeout) { | ||
| newTenantList.push(tenname); | ||
| } | ||
| return cb(null); | ||
| }; | ||
| }; | ||
| opts = { | ||
| options: { | ||
| config: cfgData.configPath, | ||
| tenant: tenname | ||
| } | ||
| }; | ||
| handler = new events.EventEmitter(); | ||
| handler.on('success', function(obj) { | ||
| return singleCb(false)(); | ||
| }); | ||
| handler.on('failure', function(obj) { | ||
| switch (obj.type) { | ||
| case 'config': | ||
| output.warn("Can't read \"" + obj.path + "\"."); | ||
| break; | ||
| case 'exists': | ||
| output.warn("Client is already registered."); | ||
| break; | ||
| case 'message': | ||
| output.warn(obj.message, obj.error); | ||
| break; | ||
| default: | ||
| output.warn("Unknown register error " + obj); | ||
| } | ||
| output.warn("Proceeding anyways."); | ||
| return singleCb(false, "register failure")(); | ||
| }); | ||
| manage.commands(handler).register.run(opts); | ||
| return setTimeout(singleCb(true), 5000); | ||
| }, function(err) { | ||
| var str; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if (newTenantList.length > 0) { | ||
| str = JSON.stringify(newTenantList, null, 2) + '\n'; | ||
| return fs.writeFile(savePath, str, { | ||
| encoding: 'utf8' | ||
| }, function(err) { | ||
| if (err != null) { | ||
| output.warn("couldn't write 'restore' file: " + err); | ||
| } | ||
| output.warn("Couldn't register some tenants during healing restart. Will attempt again in 30 seconds."); | ||
| return setTimeout((function() { | ||
| util.restart(daemon.service); | ||
| return process.exit(-1); | ||
| }), 30000); | ||
| }); | ||
| } else { | ||
| return cb(null); | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| handle = function(cl) { | ||
| cl.on('error', function() {}); | ||
| cl.on('start', function() { | ||
| return startHeartbeat(function() { | ||
| return output.log('Client started.'); | ||
| }); | ||
| }); | ||
| cl.on('close', function(hadErr) { | ||
| return stopHeartbeat(function() { | ||
| return output.log('Client closed.'); | ||
| }); | ||
| }); | ||
| cl.on('listening', function() { | ||
| return output.log('Client listening.'); | ||
| }); | ||
| cl.on('deaf', function() { | ||
| return output.log('Client stopped listening.'); | ||
| }); | ||
| cl.on('retry', function(index) { | ||
| return output.log('Retrying connection.'); | ||
| }); | ||
| cl.on('willRetry', function(index, timeout) { | ||
| return output.log("Connnection failed (" + index + "). Will retry in " + timeout + " seconds."); | ||
| }); | ||
| cl.on('command', function(cmd, c) { | ||
| var data, e, spind; | ||
| output.log('Client command', cmd); | ||
| switch (false) { | ||
| case cmd !== 'STATUS': | ||
| return c.end(cl.status()); | ||
| case cmd !== 'STOP': | ||
| return stopHeartbeat(function() { | ||
| return cl.end(function() { | ||
| return cl.deafen(); | ||
| }); | ||
| }); | ||
| case cmd !== 'RELOAD': | ||
| return unload(function() { | ||
| return load(function() {}); | ||
| }); | ||
| case cmd !== 'PATCH': | ||
| return unload; | ||
| case cmd !== 'PING': | ||
| return cl.backchannel.ping(function(err, stats) { | ||
| if (err != null) { | ||
| return c.end({ | ||
| type: 'error', | ||
| error: "Can't ping: " + err | ||
| }); | ||
| } else { | ||
| return c.end(stats); | ||
| } | ||
| }); | ||
| case !_.startsWith(cmd, 'LOG'): | ||
| spind = cmd.indexOf(' '); | ||
| if (spind <= 0) { | ||
| output.setLevel('debug'); | ||
| return c.end({ | ||
| success: true, | ||
| level: 'debug' | ||
| }); | ||
| } else { | ||
| level = cmd.substr(spind + 1); | ||
| output.setLevel(level); | ||
| return c.end({ | ||
| success: true, | ||
| level: level | ||
| }); | ||
| } | ||
| break; | ||
| case !_.startsWith(cmd, 'UPGRADE'): | ||
| spind = cmd.indexOf(' '); | ||
| if (spind <= 0) { | ||
| return c.end({ | ||
| success: false, | ||
| error: "No path given for upgrade" | ||
| }); | ||
| } | ||
| try { | ||
| data = JSON.parse(cmd.substr(spind + 1)); | ||
| } catch (error) { | ||
| e = error; | ||
| return c.end({ | ||
| success: false, | ||
| error: e | ||
| }); | ||
| } | ||
| return patches.applyPatch(data.patch, data.path, cl.config.configPath, function(err) { | ||
| if (err != null) { | ||
| return c.end({ | ||
| success: false, | ||
| error: "Patch failed: " + err | ||
| }); | ||
| } else { | ||
| c.end({ | ||
| success: true | ||
| }); | ||
| stopHeartbeat(function() { | ||
| return cl.end(function() { | ||
| return cl.deafen(); | ||
| }); | ||
| }); | ||
| return util.restart(service); | ||
| } | ||
| }); | ||
| default: | ||
| return output.warn("Unknown command " + cmd); | ||
| } | ||
| }); | ||
| cl.listener.on('error', function(err) { | ||
| return output.prettyListen(SEVERE, err, cl.config); | ||
| }); | ||
| cl.listener.on('close', function() { | ||
| return output.log('Command listener closed.'); | ||
| }); | ||
| cl.listener.on('connection', function(c) { | ||
| output.log('Command stream opened.'); | ||
| c.on('error', function(err) { | ||
| return output.prettyListen(SEVERE, err, cl.config); | ||
| }); | ||
| c.on('close', function() { | ||
| return output.log('Command stream closed.'); | ||
| }); | ||
| return c.on('data', function(data) { | ||
| return output.log('Command stream data', data); | ||
| }); | ||
| }); | ||
| cl.listener.on('listening', function(addr) { | ||
| return output.log('Command listening on', addr); | ||
| }); | ||
| cl.backchannel.on('error', function(err) { | ||
| return output.prettyBacktalk(SEVERE, err, cl.config); | ||
| }); | ||
| cl.backchannel.on('close', function() { | ||
| return output.log('Backchannel closed.'); | ||
| }); | ||
| cl.backchannel.on('connected', function() { | ||
| return output.log('Backchannel connected.'); | ||
| }); | ||
| cl.backchannel.on('handshake', function() { | ||
| return output.log('Backchannel handshake done.'); | ||
| }); | ||
| cl.backchannel.on('save', function(err, epath) { | ||
| if (err != null) { | ||
| return output.warn("Couldn't save endpoint path " + epath, err); | ||
| } else { | ||
| return output.log("Saved endpoint to " + epath); | ||
| } | ||
| }); | ||
| cl.tunnel.on('error', function(err) { | ||
| output.prettyTunnel(SEVERE, err, cl.config); | ||
| return healer.heal(cl.config, daemon); | ||
| }); | ||
| cl.tunnel.on('close', function(err) { | ||
| return output.log('Tunnel closed.'); | ||
| }); | ||
| cl.tunnel.on('connected', function() { | ||
| return output.log('Tunnel connected.'); | ||
| }); | ||
| return cl.tunnel.on('received', function(c) { | ||
| output.log('Tunnel received.'); | ||
| c.on('bound', function() { | ||
| return output.log('Local connection tunneled.'); | ||
| }); | ||
| c.on('unbound', function() { | ||
| return output.log('Local connection closed.'); | ||
| }); | ||
| return c.on('error', function(err) { | ||
| return output.prettyTunnel(SEVERE, err, cl.config); | ||
| }); | ||
| }); | ||
| }; | ||
| startRest = function() { | ||
| return config.readConfig(opt.options.config, function(err, cfgPath, cfgData) { | ||
| var apiPort, ref, ref1; | ||
| if (err != null) { | ||
| return output.warn("Can't start REST API: " + err); | ||
| } | ||
| apiPort = (ref = (ref1 = cfgData.client) != null ? ref1.apiPort : void 0) != null ? ref : 8100; | ||
| output.log("Starting REST API server on " + apiPort); | ||
| return restapi.start(apiPort, opt, output); | ||
| }); | ||
| }; | ||
| errors.init(cfgPath, 'client', level, function(err, out) { | ||
| if (err != null) { | ||
| return console.error('Unable to initialize logger', err); | ||
| } | ||
| daemon.output = output = out; | ||
| startRest(); | ||
| return main(); | ||
| }); | ||
| }).call(this); |
| (function() { | ||
| var async, config, errors, exec, fs, log, mkdirp, path, readQbLocation, ref, regedit, runQbDelayed, runQbForever, spawn, start, startLater, stop; | ||
| async = require('async'); | ||
| ref = require('child_process'), exec = ref.exec, spawn = ref.spawn; | ||
| config = require('../config'); | ||
| path = require('path'); | ||
| fs = require('fs'); | ||
| errors = require('../errors'); | ||
| log = errors.create('info'); | ||
| mkdirp = require('mkdirp'); | ||
| regedit = require('regedit'); | ||
| exports.stop = stop = function() {}; | ||
| exports.start = start = function(configData) { | ||
| return setTimeout((function() { | ||
| return startLater(configData); | ||
| }), 20000); | ||
| }; | ||
| startLater = function(configData) { | ||
| if (configData.qbmonitor == null) { | ||
| configData.qbmonitor = { | ||
| shouldRun: false | ||
| }; | ||
| } | ||
| return readQbLocation(configData, function(err, loc) { | ||
| if (err != null) { | ||
| log.log("Couldn't read external plugin."); | ||
| return start(configData); | ||
| } else { | ||
| return runQbForever(configData, loc, function(err) { | ||
| if (err != null) { | ||
| log.warn("QB Monitor exited prematurely: " + err); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| }; | ||
| readQbLocation = function(configData, cb) { | ||
| return regedit.list('HKCU\\SOFTWARE\\CE', function(err, result) { | ||
| var ifldr, ref1, ref2, ref3; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| ifldr = (ref1 = result['HKCU\\SOFTWARE\\CE']) != null ? (ref2 = ref1.values) != null ? (ref3 = ref2.InstallFolder) != null ? ref3.value : void 0 : void 0 : void 0; | ||
| if (ifldr) { | ||
| ifldr = ifldr + '\\QB Connector.exe'; | ||
| } | ||
| if (ifldr != null) { | ||
| return cb(null, ifldr); | ||
| } else { | ||
| return cb("No QB location could be found."); | ||
| } | ||
| }); | ||
| }; | ||
| runQbForever = function(configData, loc, cb) { | ||
| return setTimeout((function() { | ||
| return runQbDelayed(configData, loc, cb); | ||
| }), 15000); | ||
| }; | ||
| runQbDelayed = function(configData, loc, cb) { | ||
| var args, cmd, err, g2cdir, logDirPath, out; | ||
| cmd = loc; | ||
| g2cdir = path.dirname(configData.configPath); | ||
| args = []; | ||
| logDirPath = path.join(g2cdir, 'logs'); | ||
| out = 'ignore'; | ||
| err = 'ignore'; | ||
| return mkdirp(logDirPath, function(err) { | ||
| var child; | ||
| if (err != null) { | ||
| log.warn("Unable to make log dir for connector logs."); | ||
| } else { | ||
| out = fs.openSync(path.join(logDirPath, './ceconnector.log'), 'w'); | ||
| err = fs.openSync(path.join(logDirPath, './ceconnector.err'), 'w'); | ||
| } | ||
| child = spawn(cmd, args, { | ||
| detached: false, | ||
| stdio: ['ignore', out, err] | ||
| }); | ||
| return child.on('exit', function(code) { | ||
| log.warn("Child QB exited with code " + code + "."); | ||
| return cb(null); | ||
| }); | ||
| }); | ||
| }; | ||
| }).call(this); |
| (function() { | ||
| var _, argOpts, cloneOpts, command, events, manage, responseHandler, sendTo, | ||
| slice = [].slice; | ||
| _ = require('lodash'); | ||
| events = require('events'); | ||
| manage = require('./manage'); | ||
| exports.start = function(port, opts, output) { | ||
| var allowCrossDomain, app, bodyParser, cmd, express; | ||
| express = require('express'); | ||
| bodyParser = require('body-parser'); | ||
| allowCrossDomain = function(req, res, next) { | ||
| var ref, ref1; | ||
| res.header('Access-Control-Allow-Credentials', true); | ||
| res.header('Access-Control-Allow-Origin', typeof req.headers === "function" ? req.headers(typeof origin !== "undefined" && origin !== null ? origin : '*') : void 0); | ||
| res.header('Access-Control-Allow-Methods', 'PUT,OPTIONS,GET,DELETE,POST'); | ||
| res.header('Access-Control-Allow-Headers', (ref = (ref1 = req.headers) != null ? ref1['Access-Control-Request-Headers'] : void 0) != null ? ref : 'x-requested-with,content-type,authorization,accept'); | ||
| res.header('Access-Control-Max-Age', 1728000); | ||
| return next(); | ||
| }; | ||
| app = express(); | ||
| app.use(allowCrossDomain); | ||
| app.use(bodyParser.json({ | ||
| limit: '500kb' | ||
| })); | ||
| app.set('etag', false); | ||
| app.options('*', function(req, res, next) { | ||
| return res.send(200); | ||
| }); | ||
| cmd = command(opts, output); | ||
| app.get('/status', cmd.status); | ||
| app.get('/tenants', cmd.tenants); | ||
| app["delete"]('/tenants/:id', cmd.evict); | ||
| app.post('/tenants', cmd.lease); | ||
| app.get('/ping', cmd.ping); | ||
| app.get('/version', cmd.version); | ||
| app.post('/register', cmd.registerAny); | ||
| app.post('/register/:id', cmd.register); | ||
| app.post('/unregister/:id', cmd.unregister); | ||
| app.get('/config', cmd.config); | ||
| app.get('/config/:id', cmd.configTen); | ||
| app.post('/reload', cmd.reload); | ||
| app.post('/upgrade', cmd.upgrade); | ||
| app.get('/check', cmd.check); | ||
| app.post('/log', cmd.log); | ||
| app.post('/sping', cmd.sping); | ||
| app.post('/stop', cmd.stop); | ||
| app.post('/start', cmd.start); | ||
| app.listen(port); | ||
| return output.log('REST API started'); | ||
| }; | ||
| command = function(opts, output) { | ||
| var commands, k, loopfn, v, workers; | ||
| workers = { | ||
| tenants: function(manager, req, res) { | ||
| return manager.tenants.run(opts); | ||
| }, | ||
| status: function(manager, req, res) { | ||
| return manager.status.run(opts); | ||
| }, | ||
| evict: function(manager, req, res, next) { | ||
| return manager.evict.run(cloneOpts(opts, { | ||
| tenant: req.params.id | ||
| })); | ||
| }, | ||
| registerAny: function(manager, req, res, next) { | ||
| return manager.register.run(opts); | ||
| }, | ||
| register: function(manager, req, res, next) { | ||
| return manager.register.run(cloneOpts(opts, { | ||
| tenant: req.params.id | ||
| })); | ||
| }, | ||
| unregister: function(manager, req, res, next) { | ||
| return manager.unregister.run(cloneOpts(opts, { | ||
| tenant: req.params.id | ||
| })); | ||
| }, | ||
| lease: function(manager, req, res, next) { | ||
| var params; | ||
| params = {}; | ||
| if (req.body.host != null) { | ||
| params.host = req.body.host; | ||
| } | ||
| if (req.body.port != null) { | ||
| params.port = req.body.port; | ||
| } | ||
| return manager.lease.run(cloneOpts(opts, params)); | ||
| }, | ||
| ping: function(manager, req, res) { | ||
| return manager.ping.run(opts); | ||
| }, | ||
| version: function(manager, req, res) { | ||
| return manager.version.run(opts); | ||
| }, | ||
| config: function(manager, req, res) { | ||
| return manager.config.run(opts); | ||
| }, | ||
| configTen: function(manager, req, res) { | ||
| return manager.config.run(cloneOpts(opts, { | ||
| tenant: req.params.id | ||
| })); | ||
| }, | ||
| reload: function(manager, req, res) { | ||
| return manager.reload.run(opts); | ||
| }, | ||
| upgrade: function(manager, req, res) { | ||
| return manager.upgrade.run(opts); | ||
| }, | ||
| check: function(manager, req, res) { | ||
| return manager.check.run(opts); | ||
| }, | ||
| log: function(manager, req, res) { | ||
| return manager.log.run(argOpts(opts, ['log', req.body.msg])); | ||
| }, | ||
| sping: function(manager, req, res) { | ||
| return manager.sping.run(opts); | ||
| }, | ||
| stop: function(manager, req, res) { | ||
| return manager.stop.run(opts); | ||
| }, | ||
| start: function(manager, req, res) { | ||
| return manager.start.run(opts); | ||
| } | ||
| }; | ||
| commands = {}; | ||
| for (k in workers) { | ||
| v = workers[k]; | ||
| loopfn = function(k, v) { | ||
| return commands[k] = function(req, res) { | ||
| var handler, manager; | ||
| handler = responseHandler(req, res, output); | ||
| manager = manage.commands(handler); | ||
| return v(manager, req, res); | ||
| }; | ||
| }; | ||
| loopfn(k, v); | ||
| } | ||
| return commands; | ||
| }; | ||
| responseHandler = function(req, res, output) { | ||
| var emitter, send; | ||
| send = sendTo(res); | ||
| emitter = new events.EventEmitter(); | ||
| emitter.on('success', function(obj) { | ||
| return send(null, { | ||
| success: _.assign({ | ||
| success: true | ||
| }, obj) | ||
| }); | ||
| }); | ||
| emitter.on('failure', function(obj) { | ||
| return send({ | ||
| status: 500, | ||
| error: obj | ||
| }); | ||
| }); | ||
| emitter.on('part', function() { | ||
| var part; | ||
| part = 1 <= arguments.length ? slice.call(arguments, 0) : []; | ||
| return output.log.apply(output, part); | ||
| }); | ||
| emitter.on('multi', function(multi) { | ||
| var i, len, m, results; | ||
| results = []; | ||
| for (i = 0, len = multi.length; i < len; i++) { | ||
| m = multi[i]; | ||
| results.push(output.log.apply(output, m)); | ||
| } | ||
| return results; | ||
| }); | ||
| return emitter; | ||
| }; | ||
| sendTo = function(res) { | ||
| return function(err, item) { | ||
| var ref; | ||
| if (err != null) { | ||
| if (err.status != null) { | ||
| res.status(err.status); | ||
| err = err.error; | ||
| } else { | ||
| res.status(500); | ||
| } | ||
| return res.send({ | ||
| message: 'Data Error', | ||
| error: err | ||
| }); | ||
| } else { | ||
| if (item == null) { | ||
| return res.status(404).send({ | ||
| message: 'Not found' | ||
| }); | ||
| } else if (item.failure != null) { | ||
| return res.status((ref = item.status) != null ? ref : 500).send(item.failure); | ||
| } else { | ||
| return res.send(item.success); | ||
| } | ||
| } | ||
| }; | ||
| }; | ||
| cloneOpts = function(opts, more) { | ||
| var importOpts, opts2; | ||
| opts2 = _.cloneDeep(opts); | ||
| importOpts = function(obj, src) { | ||
| var key, own; | ||
| own = {}.hasOwnProperty; | ||
| for (key in src) { | ||
| if (own.call(src, key)) { | ||
| obj[key] = src[key]; | ||
| } | ||
| } | ||
| return obj; | ||
| }; | ||
| importOpts(opts2.options, more); | ||
| return opts2; | ||
| }; | ||
| argOpts = function(opts, argv) { | ||
| var opts2; | ||
| opts2 = _.cloneDeep(opts); | ||
| opts2.argv = argv; | ||
| return opts2; | ||
| }; | ||
| }).call(this); |
| (function() { | ||
| var _, connect, emitter, events, iceptPath, net, open, ssh2; | ||
| _ = require('lodash'); | ||
| events = require('events'); | ||
| net = require('net'); | ||
| ssh2 = require('ssh2'); | ||
| emitter = require('../emitter'); | ||
| exports.iceptPath = iceptPath = '/-g2c-intercept'; | ||
| exports.create = function() { | ||
| var protocol; | ||
| protocol = new events.EventEmitter(); | ||
| protocol.connect = function(bt, tenant) { | ||
| return connect(protocol, bt, tenant); | ||
| }; | ||
| return protocol; | ||
| }; | ||
| connect = function(protocol, bt, tenant) { | ||
| protocol.config = bt.config; | ||
| return open(protocol, tenant); | ||
| }; | ||
| open = function(protocol, tenant) { | ||
| var cfgData, conn, ended; | ||
| cfgData = protocol.config; | ||
| _.defaultsDeep(cfgData, { | ||
| bindAddr: 'localhost', | ||
| local: { | ||
| host: 'localhost', | ||
| port: 8080 | ||
| } | ||
| }); | ||
| if (tenant == null) { | ||
| tenant = {}; | ||
| } | ||
| if (tenant.bindPort == null) { | ||
| tenant.bindPort = 11080; | ||
| } | ||
| _.defaultsDeep(tenant, { | ||
| bindPort: 11080, | ||
| config: { | ||
| local: { | ||
| host: cfgData.local.host, | ||
| port: cfgData.local.port | ||
| } | ||
| } | ||
| }); | ||
| ended = false; | ||
| protocol.end = function() {}; | ||
| conn = new ssh2.Client(); | ||
| conn.on('error', function(err) { | ||
| return protocol.emit('error', err); | ||
| }); | ||
| conn.on('close', function(hadErr) { | ||
| return protocol.emit('close', hadErr); | ||
| }); | ||
| conn.on('ready', function() { | ||
| return conn.forwardIn(cfgData.bindAddr, tenant.bindPort, function(err) { | ||
| if (err != null) { | ||
| protocol.emit('error', err); | ||
| return conn.end(); | ||
| } | ||
| protocol.emit('connected'); | ||
| return protocol.end = function() { | ||
| if (!ended) { | ||
| conn.end(); | ||
| } | ||
| return ended = true; | ||
| }; | ||
| }); | ||
| }); | ||
| conn.on('tcp connection', function(info, accept, reject) { | ||
| var bindToLocal, channel, le; | ||
| channel = null; | ||
| bindToLocal = function(preamble) { | ||
| var bound, local, ref; | ||
| channel.unlisten(); | ||
| bound = false; | ||
| local = new net.Socket(); | ||
| local.on('error', function(err) { | ||
| le.emit('error', err); | ||
| if (channel != null) { | ||
| channel.end(); | ||
| } | ||
| return channel = null; | ||
| }); | ||
| local.on('close', function(err) { | ||
| if (bound) { | ||
| le.emit('unbound'); | ||
| } | ||
| if (channel != null) { | ||
| channel.end(); | ||
| } | ||
| return channel = null; | ||
| }); | ||
| if (((ref = cfgData.qbmonitor) != null ? ref.local : void 0) != null) { | ||
| tenant.config.local = cfgData.qbmonitor.local; | ||
| } | ||
| return local.connect(tenant.config.local.port, tenant.config.local.host, function() { | ||
| if (preamble != null) { | ||
| local.write(preamble); | ||
| } | ||
| local.pipe(channel).pipe(local); | ||
| bound = true; | ||
| return le.emit('bound'); | ||
| }); | ||
| }; | ||
| le = new events.EventEmitter(); | ||
| le.info = info; | ||
| protocol.emit('received', le); | ||
| if ((channel = accept()) != null) { | ||
| emitter.augment(channel); | ||
| channel.on('error', function(err) { | ||
| return le.emit('error', err); | ||
| }); | ||
| channel.listenOn('readable', function() { | ||
| var best, buf, readCt, some, target; | ||
| readCt = 0; | ||
| best = "GET " + iceptPath; | ||
| buf = new Buffer(best.length); | ||
| target = new Buffer(best, 'utf8'); | ||
| if ((some = channel.read(best.length - readCt)) == null) { | ||
| if ((some = channel.read()) != null) { | ||
| some.copy(buf, readCt); | ||
| readCt += some.length; | ||
| if (!buf.equals(target.slice(0, readCt))) { | ||
| return bindToLocal(buf.slice(0, readCt)); | ||
| } | ||
| } | ||
| } else { | ||
| some.copy(buf, readCt); | ||
| readCt += some.length; | ||
| if (buf.length !== best.length) { | ||
| return bindToLocal(buf.slice(0, readCt)); | ||
| } else { | ||
| if (buf.equals(target)) { | ||
| channel.unlisten(); | ||
| return channel.end(['HTTP/1.1 200 OK', 'Content-Length: 18', 'Connection: close', '', '{"success": true}\n'].join('\r\n')); | ||
| } else { | ||
| return bindToLocal(buf); | ||
| } | ||
| } | ||
| } | ||
| }); | ||
| return channel.listenOn('end', function() { | ||
| return bindToLocal(null); | ||
| }); | ||
| } else { | ||
| return conn.end(); | ||
| } | ||
| }); | ||
| return conn.connect({ | ||
| host: cfgData.host, | ||
| port: cfgData.port || 22, | ||
| username: cfgData.username, | ||
| privateKey: cfgData.keydata, | ||
| passphrase: cfgData.passphrase, | ||
| keepaliveInterval: 30000, | ||
| keepaliveCountMax: 3 | ||
| }); | ||
| }; | ||
| }).call(this); |
| (function() { | ||
| var _, config, guid, os, restart, selfRestartUnix, selfRestartWin, spawn, verifyConfig; | ||
| _ = require('lodash'); | ||
| os = require('os'); | ||
| config = require('../config'); | ||
| spawn = require('child_process').spawn; | ||
| exports.guid = guid = function() { | ||
| var s4; | ||
| s4 = function() { | ||
| return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); | ||
| }; | ||
| return s4() + s4() + s4() + s4(); | ||
| }; | ||
| exports.restart = restart = function(service) { | ||
| if (!service) { | ||
| if (os.platform() === 'win32') { | ||
| return selfRestartWin(); | ||
| } else { | ||
| return selfRestartUnix(); | ||
| } | ||
| } | ||
| }; | ||
| exports.selfRestartUnix = selfRestartUnix = function() { | ||
| var child, shargs; | ||
| shargs = [ | ||
| '-c', "sleep 3 ; " + _.map(process.argv, function(a) { | ||
| return "\"" + a + "\""; | ||
| }).join(' ') | ||
| ]; | ||
| child = spawn('sh', shargs, { | ||
| detached: true, | ||
| stdio: 'inherit' | ||
| }); | ||
| return child.unref(); | ||
| }; | ||
| exports.selfRestartWin = selfRestartWin = function() { | ||
| var child, shargs; | ||
| shargs = [ | ||
| '/c', "timeout 3 >nul & " + _.map(process.argv, function(a) { | ||
| return a.replace(/\ /g, '\\ '); | ||
| }).join(' ') | ||
| ]; | ||
| child = spawn('cmd.exe', shargs, { | ||
| detached: true, | ||
| stdio: 'inherit' | ||
| }); | ||
| return child.unref(); | ||
| }; | ||
| exports.verifyConfig = verifyConfig = function(configPath, configData, cb) { | ||
| return config.readRegistration(configPath, configData, function(err, regData) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| configData.client.registration = regData; | ||
| return config.readKey(configPath, configData, function(err, keydata) { | ||
| configData.client.keydata = keydata; | ||
| return cb(err, !(_.isEmpty(regData))); | ||
| }); | ||
| }); | ||
| }; | ||
| }).call(this); |
-444
| (function() { | ||
| var _, async, createConfig, defaultConfig, fs, guid, maxTenantIndex, mkdirp, parseToml, path, pkg, readData, readQbData, tenantsType, toml, tweak, version, writeQbData; | ||
| _ = require('lodash'); | ||
| async = require('async'); | ||
| fs = require('fs'); | ||
| path = require('path'); | ||
| mkdirp = require('mkdirp'); | ||
| toml = require('toml'); | ||
| pkg = require('../package.json'); | ||
| exports.version = version = function() { | ||
| var ref; | ||
| return { | ||
| name: 'Ground2Cloud', | ||
| version: pkg.version, | ||
| display: (ref = pkg.displayVersion) != null ? ref : pkg.version | ||
| }; | ||
| }; | ||
| defaultConfig = "[server]\nproxyPort = 8090\nsecureProxyPort = 8453\nbackPort = 3014\nlistenPort = 3016\nregistryfile = \"./registry\"\nregistryUrl = \"redis://localhost:6379\"\nstatsUrl = \"elastic://localhost:9200\"\napiPort = 8101\n\n[server.vitae]\nenvironment = \"local\"\n# environment = \"staging\"\nendpoint = \"http://REGID.localhost:8090\"\n# endpoint = \"https://REGID.g2c-staging.cloud-elements.com\"\n\n[client]\nhost = \"localhost\"\nport = 22\nbackPort = 3014\nusername = \"username\"\nlistenPort = 3015\nbindAddr = \"0.0.0.0\"\napiPort = 8100\n\n[client.vitae]\nbrand = \"ce\"\n\n[client.local]\nhost = \"localhost\"\nport = 8088\n"; | ||
| exports.readConfig = function(configPath, cb) { | ||
| return createConfig(configPath, function(err, made, configPath) { | ||
| if (err != null) { | ||
| return cb(err, configPath); | ||
| } | ||
| return readData(configPath, cb); | ||
| }); | ||
| }; | ||
| exports.pickConfig = function(configPath) { | ||
| var configDirPath, home; | ||
| if (configPath != null) { | ||
| return configPath; | ||
| } | ||
| home = process.env.HOME || process.env.USERPROFILE; | ||
| configDirPath = path.join(home, '.petit'); | ||
| return path.join(configDirPath, 'config.toml'); | ||
| }; | ||
| exports.createConfig = createConfig = function(configPath, cb) { | ||
| var configDirPath, home; | ||
| if (configPath == null) { | ||
| home = process.env.HOME || process.env.USERPROFILE; | ||
| configDirPath = path.join(home, '.petit'); | ||
| configPath = path.join(configDirPath, 'config.toml'); | ||
| } else { | ||
| configDirPath = path.dirname(configPath); | ||
| } | ||
| return mkdirp(configDirPath, function(err) { | ||
| if (err != null) { | ||
| return cb(err, false, configPath); | ||
| } | ||
| return fs.exists(configPath, function(exists) { | ||
| if (!exists) { | ||
| return fs.writeFile(configPath, defaultConfig, { | ||
| encoding: 'utf8' | ||
| }, function(err) { | ||
| return cb(err, true, configPath); | ||
| }); | ||
| } else { | ||
| return cb(null, false, configPath); | ||
| } | ||
| }); | ||
| }); | ||
| }; | ||
| exports.readRegistration = function(configPath, configData, cb) { | ||
| var regs, tenPath; | ||
| tenPath = path.join(path.dirname(configPath), 'tenants'); | ||
| regs = []; | ||
| return mkdirp(tenPath, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return fs.readdir(tenPath, function(err, dirs) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return async.each(dirs, function(dir, cb) { | ||
| var regfile, tendir; | ||
| tendir = path.join(tenPath, dir); | ||
| regfile = path.join(tendir, 'registration'); | ||
| return fs.exists(regfile, function(exists) { | ||
| if (exists) { | ||
| return fs.readFile(regfile, { | ||
| encoding: 'utf8' | ||
| }, function(err, fdata) { | ||
| var e, obj; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| try { | ||
| obj = JSON.parse(fdata); | ||
| obj.dir = tendir; | ||
| regs.push(obj); | ||
| } catch (error) { | ||
| e = error; | ||
| return cb(e); | ||
| } | ||
| return cb(null); | ||
| }); | ||
| } else { | ||
| return cb(null); | ||
| } | ||
| }); | ||
| }, function(err) { | ||
| return cb(err, regs); | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| exports.readKey = function(configPath, configData, cb) { | ||
| var keyPath; | ||
| keyPath = path.join(path.dirname(configPath), 'key'); | ||
| return fs.exists(keyPath, function(exists) { | ||
| if (exists) { | ||
| return fs.readFile(keyPath, { | ||
| encoding: 'utf8' | ||
| }, cb); | ||
| } else { | ||
| return cb(null); | ||
| } | ||
| }); | ||
| }; | ||
| readData = function(configPath, cb) { | ||
| return fs.readFile(configPath, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| if (err != null) { | ||
| return cb(err, configPath); | ||
| } | ||
| return parseToml(configPath, data, function(err, configPath, parsed) { | ||
| var runtimePath; | ||
| runtimePath = path.join(path.dirname(configPath), 'runtime.json'); | ||
| return fs.exists(runtimePath, function(exists) { | ||
| if (!exists) { | ||
| return cb(err, configPath, parsed); | ||
| } | ||
| return fs.readFile(runtimePath, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| if (err != null) { | ||
| return cb(err, configPath); | ||
| } | ||
| try { | ||
| parsed.runtime = JSON.parse(data); | ||
| } catch (error) { | ||
| err = error; | ||
| return cb(err, configPath); | ||
| } | ||
| return readQbData(configPath, parsed, cb); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| readQbData = function(configPath, parsed, cb) { | ||
| var qbPath; | ||
| qbPath = path.join(path.dirname(configPath), 'qb.json'); | ||
| return fs.exists(qbPath, function(exists) { | ||
| if (!exists) { | ||
| return cb(null, configPath, parsed); | ||
| } | ||
| return fs.readFile(qbPath, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| if (err != null) { | ||
| return cb(err, configPath); | ||
| } | ||
| try { | ||
| if (parsed.client == null) { | ||
| parsed.client = {}; | ||
| } | ||
| parsed.client.qbmonitor = JSON.parse(data); | ||
| } catch (error) { | ||
| err = error; | ||
| return cb(err, configPath, parsed); | ||
| } | ||
| return cb(null, configPath, parsed); | ||
| }); | ||
| }); | ||
| }; | ||
| exports.writeQbData = writeQbData = function(clconfigData, cb) { | ||
| var qbPath, qbdata; | ||
| qbPath = path.join(path.dirname(clconfigData.configPath), 'qb.json'); | ||
| qbdata = JSON.stringify(clconfigData.qbmonitor, null, 2); | ||
| return fs.writeFile(qbPath, qbdata, { | ||
| encoding: 'utf8' | ||
| }, cb); | ||
| }; | ||
| parseToml = function(configPath, data, cb) { | ||
| var e, parsed, ref, ref1; | ||
| try { | ||
| parsed = tweak(configPath, toml.parse(data)); | ||
| parsed.configPath = configPath; | ||
| if ((ref = parsed.server) != null) { | ||
| ref.configPath = configPath; | ||
| } | ||
| if ((ref1 = parsed.client) != null) { | ||
| ref1.configPath = configPath; | ||
| } | ||
| } catch (error) { | ||
| e = error; | ||
| return cb(e, configPath); | ||
| } | ||
| return cb(null, configPath, parsed); | ||
| }; | ||
| tweak = function(configPath, data) { | ||
| var dirname; | ||
| dirname = path.dirname(configPath); | ||
| if (_.isString(data)) { | ||
| if (_.startsWith(data, './')) { | ||
| return path.join(dirname, data.substr(2)); | ||
| } else if (_.startsWith(data, '../')) { | ||
| return path.join(path.dirname(dirname), data.substr(3)); | ||
| } else { | ||
| return data; | ||
| } | ||
| } else if (_.isObject(data)) { | ||
| return _.forOwn(data, function(v, k) { | ||
| return data[k] = tweak(configPath, v); | ||
| }); | ||
| } else { | ||
| return data; | ||
| } | ||
| }; | ||
| tenantsType = function(cfgData, reg, cb) { | ||
| var tenPath; | ||
| tenPath = path.join(path.dirname(cfgData.configPath), 'tenants'); | ||
| return mkdirp(tenPath, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return fs.readdir(tenPath, function(err, dirs) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return async.filter(dirs, function(dir, cb) { | ||
| return fs.readdir(path.join(tenPath, dir), function(err, files) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return cb(null, (files.indexOf('registration') === -1) === reg); | ||
| }); | ||
| }, function(err, dirs) { | ||
| return cb(err, tenPath, dirs); | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| exports.unregisteredTenants = function(cfgData, cb) { | ||
| return tenantsType(cfgData, true, cb); | ||
| }; | ||
| exports.registeredTenants = function(cfgData, cb) { | ||
| return tenantsType(cfgData, false, cb); | ||
| }; | ||
| exports.allTenants = function(cfgData, cb) { | ||
| var tenPath; | ||
| tenPath = path.join(path.dirname(cfgData.configPath), 'tenants'); | ||
| return mkdirp(tenPath, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return fs.readdir(tenPath, function(err, dirs) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return async.map(dirs, function(dir, cb) { | ||
| var regFile, tendir; | ||
| tendir = path.join(tenPath, dir); | ||
| regFile = path.join(tendir, 'registration'); | ||
| return fs.exists(regFile, function(exists) { | ||
| if (exists) { | ||
| return fs.readFile(regFile, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| var e, obj; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| try { | ||
| obj = JSON.parse(data); | ||
| } catch (error) { | ||
| e = error; | ||
| return cb(e); | ||
| } | ||
| return cb(null, { | ||
| dir: tendir, | ||
| name: dir, | ||
| registered: true, | ||
| registeredId: obj.id, | ||
| registeredPort: obj.port | ||
| }); | ||
| }); | ||
| } else { | ||
| return cb(null, { | ||
| dir: tendir, | ||
| name: dir, | ||
| registered: false | ||
| }); | ||
| } | ||
| }); | ||
| }, function(err, tens) { | ||
| return cb(err, tenPath, tens); | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| exports.tenantConfig = function(tname, cfgData, cb) { | ||
| var tenPath; | ||
| tenPath = path.join(path.dirname(cfgData.configPath), 'tenants'); | ||
| return mkdirp(tenPath, function(err) { | ||
| var tCfgPath, tendir; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| tendir = path.join(tenPath, tname); | ||
| tCfgPath = path.join(tendir, 'config.toml'); | ||
| return fs.exists(tendir, function(exists) { | ||
| if (exists) { | ||
| return fs.exists(tCfgPath, function(exists) { | ||
| if (exists) { | ||
| return fs.readFile(tCfgPath, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| var e, obj; | ||
| try { | ||
| obj = toml.parse(data); | ||
| } catch (error) { | ||
| e = error; | ||
| return cb(e); | ||
| } | ||
| return cb(null, tCfgPath, obj); | ||
| }); | ||
| } else { | ||
| return cb(null, tCfgPath, {}); | ||
| } | ||
| }); | ||
| } else { | ||
| return cb("No such tenant " + tname); | ||
| } | ||
| }); | ||
| }); | ||
| }; | ||
| maxTenantIndex = function(cfgData, cb) { | ||
| var max, tenPath; | ||
| tenPath = path.join(path.dirname(cfgData.configPath), 'tenants'); | ||
| max = 0; | ||
| return mkdirp(tenPath, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return fs.readdir(tenPath, function(err, dirs) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return async.each(dirs, function(dir, cb) { | ||
| var cfgFile, tendir; | ||
| tendir = path.join(tenPath, dir); | ||
| cfgFile = path.join(tendir, 'config.toml'); | ||
| return fs.exists(cfgFile, function(exists) { | ||
| if (exists) { | ||
| return fs.readFile(cfgFile, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| var e, obj; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| try { | ||
| obj = toml.parse(data); | ||
| } catch (error) { | ||
| e = error; | ||
| return cb(e); | ||
| } | ||
| if (_.isNumber(obj.index)) { | ||
| max = Math.max(max, obj.index); | ||
| } | ||
| return cb(null); | ||
| }); | ||
| } else { | ||
| return cb(null); | ||
| } | ||
| }); | ||
| }, function(err) { | ||
| return cb(err, max); | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| exports.lease = function(cfgData, cb) { | ||
| var newten, tenPath; | ||
| tenPath = path.join(path.dirname(cfgData.configPath), 'tenants'); | ||
| newten = path.join(tenPath, guid()); | ||
| return mkdirp(newten, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return maxTenantIndex(cfgData, function(err, max) { | ||
| var cfg, cfgFile, ref, ref1, ref2, ref3; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| cfgFile = path.join(newten, 'config.toml'); | ||
| cfg = "name = \"lease\"\nindex = " + (max + 1) + "\n\n[local]\nhost = \"" + ((ref = (ref1 = cfgData.local) != null ? ref1.host : void 0) != null ? ref : 'localhost') + "\"\nport = " + ((ref2 = (ref3 = cfgData.local) != null ? ref3.port : void 0) != null ? ref2 : 8080) + "\n"; | ||
| return fs.writeFile(cfgFile, cfg, { | ||
| encoding: 'utf8' | ||
| }, function(err) { | ||
| return cb(err, newten); | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| exports.guid = guid = function() { | ||
| var s4; | ||
| s4 = function() { | ||
| return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); | ||
| }; | ||
| return s4() + s4() + s4() + s4(); | ||
| }; | ||
| }).call(this); |
| (function() { | ||
| exports.augment = function(emitter) { | ||
| emitter.listens = {}; | ||
| emitter.listenOn = function(k, fn) { | ||
| var base; | ||
| emitter.on(k, fn); | ||
| if ((base = emitter.listens)[k] == null) { | ||
| base[k] = []; | ||
| } | ||
| return emitter.listens[k].push(fn); | ||
| }; | ||
| return emitter.unlisten = function() { | ||
| var fn, fns, i, k, len, ref; | ||
| ref = emitter.listens; | ||
| for (k in ref) { | ||
| fns = ref[k]; | ||
| for (i = 0, len = fns.length; i < len; i++) { | ||
| fn = fns[i]; | ||
| emitter.removeListener(k, fn); | ||
| } | ||
| } | ||
| return emitter.listens = {}; | ||
| }; | ||
| }; | ||
| }).call(this); |
-127
| (function() { | ||
| var SEVERE, _, create, logger, mkdirp, path, pickLog, winston, | ||
| slice = [].slice; | ||
| _ = require('lodash'); | ||
| path = require('path'); | ||
| winston = require('winston'); | ||
| mkdirp = require('mkdirp'); | ||
| logger = new winston.Logger({ | ||
| transports: [new winston.transports.Console()] | ||
| }); | ||
| exports.SEVERE = SEVERE = 10; | ||
| exports.pickLog = pickLog = function(cfgpath, name, cb) { | ||
| var logDirPath, logPath; | ||
| logDirPath = path.join(path.dirname(cfgpath), 'logs'); | ||
| logPath = path.join(logDirPath, name + ".log"); | ||
| return mkdirp(logDirPath, function(err) { | ||
| return cb(err, logPath); | ||
| }); | ||
| }; | ||
| exports.init = function(cfgpath, name, level, cb) { | ||
| return pickLog(cfgpath, name, function(err, logPath) { | ||
| var returnLevel; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| logger.add(winston.transports.File, { | ||
| filename: logPath, | ||
| maxsize: 1000000, | ||
| maxFiles: 100, | ||
| tailable: true | ||
| }); | ||
| returnLevel = 0; | ||
| logger.level = (level = 'verbose') ? 'debug' : 'info'; | ||
| return cb(null, create(level)); | ||
| }); | ||
| }; | ||
| exports.create = create = function(level) { | ||
| var debug, error, log, panic, warn; | ||
| return { | ||
| exit: function() { | ||
| return process.exit(Math.max(0, returnLevel - SEVERE + 1)); | ||
| }, | ||
| level: level, | ||
| debug: debug = logger.debug, | ||
| log: log = logger.info, | ||
| warn: warn = logger.warn, | ||
| error: error = logger.error, | ||
| panic: panic = function() { | ||
| var args; | ||
| args = 1 <= arguments.length ? slice.call(arguments, 0) : []; | ||
| error.apply(null, args); | ||
| return process.exit(1); | ||
| }, | ||
| setLevel: function(level) { | ||
| return logger.level = level; | ||
| }, | ||
| prettyConfig: function(level, err) { | ||
| var returnLevel; | ||
| returnLevel = Math.max(returnLevel, level); | ||
| switch (err != null ? err.code : void 0) { | ||
| case 'ENOENT': | ||
| return warn("Couldn't access file \"" + err.path + "\"."); | ||
| default: | ||
| return warn('Unexpected configuration error:', err); | ||
| } | ||
| }, | ||
| prettyListen: function(level, err, config) { | ||
| var returnLevel; | ||
| returnLevel = Math.max(returnLevel, level); | ||
| switch (err != null ? err.code : void 0) { | ||
| case 'EADDRINUSE': | ||
| return warn("Something already listening on port " + config.listenPort + "."); | ||
| default: | ||
| return warn('Unexpected command error', err); | ||
| } | ||
| }, | ||
| prettyBacktalk: function(level, err, config) { | ||
| var ref, returnLevel; | ||
| returnLevel = Math.max(returnLevel, level); | ||
| switch (false) { | ||
| case !_.isString(err): | ||
| return warn(err); | ||
| case (err != null ? err.code : void 0) !== 'ECONNREFUSED': | ||
| return warn("Unable to connect to server at \"" + config.host + ":" + config.backPort + "\"."); | ||
| case (err != null ? (ref = err.message) != null ? ref.indexOf('CRYPT') : void 0 : void 0) === -1: | ||
| return warn('Unexpected backchannel encryption error:', err); | ||
| default: | ||
| return warn('Unexpected backchannel error:', err); | ||
| } | ||
| }, | ||
| prettyTunnel: function(level, err, config) { | ||
| var ref, returnLevel; | ||
| returnLevel = Math.max(returnLevel, level); | ||
| switch ((ref = err.error) != null ? ref.code : void 0) { | ||
| case 'ECONNREFUSED': | ||
| return warn("Unable to connect to local \"" + config.local.host + ":" + config.local.port + "\"."); | ||
| default: | ||
| return warn("Unexpected tunnel error", err); | ||
| } | ||
| }, | ||
| prettyProxy: function(level, regId, err, config) { | ||
| var returnLevel; | ||
| returnLevel = Math.max(returnLevel, level); | ||
| switch (err != null ? err.code : void 0) { | ||
| case 'EADDRINUSE': | ||
| return warn("Unable to start: address " + config.proxyPort + " already in use."); | ||
| default: | ||
| if (regId != null) { | ||
| return warn("Unexpected proxy error for " + regId, err); | ||
| } else { | ||
| return warn("Unexpected proxy error", err); | ||
| } | ||
| } | ||
| } | ||
| }; | ||
| }; | ||
| }).call(this); |
-13
| (function() { | ||
| exports.config = require('./config'); | ||
| exports.errors = require('./errors'); | ||
| exports.lock = require('./lock'); | ||
| exports.client = { | ||
| manage: require('./client/manage'), | ||
| util: require('./client/util') | ||
| }; | ||
| }).call(this); |
| (function() { | ||
| var _, events, listen, net, open; | ||
| _ = require('lodash'); | ||
| events = require('events'); | ||
| net = require('net'); | ||
| exports.create = function() { | ||
| var protocol; | ||
| protocol = new events.EventEmitter(); | ||
| protocol.listen = function(config, defPort) { | ||
| return listen(protocol, config, defPort); | ||
| }; | ||
| protocol.end = function() {}; | ||
| return protocol; | ||
| }; | ||
| listen = function(protocol, config, defPort) { | ||
| protocol.status = {}; | ||
| protocol.config = config; | ||
| return open(protocol, defPort); | ||
| }; | ||
| open = function(protocol, defPort) { | ||
| var ended, server; | ||
| _.defaults(protocol.config, { | ||
| listenPort: defPort || 3015 | ||
| }); | ||
| ended = false; | ||
| server = new net.Server(); | ||
| server.on('error', function(err) { | ||
| return protocol.emit('error', err); | ||
| }); | ||
| server.on('close', function() { | ||
| return protocol.emit('close'); | ||
| }); | ||
| server.on('listening', function() { | ||
| protocol.end = function() { | ||
| if (!ended) { | ||
| server.close(); | ||
| } | ||
| return ended = true; | ||
| }; | ||
| return protocol.emit('listening', server.address()); | ||
| }); | ||
| server.on('connection', function(socket) { | ||
| var draw, handle, readAccumulate, sp; | ||
| sp = new events.EventEmitter(); | ||
| _.assign(sp, { | ||
| reply: function(data) { | ||
| return socket.write(JSON.stringify(data)); | ||
| }, | ||
| end: function(data) { | ||
| if (data != null) { | ||
| return socket.end(JSON.stringify(data)); | ||
| } else { | ||
| return socket.end(); | ||
| } | ||
| } | ||
| }); | ||
| protocol.emit('connection', sp); | ||
| socket.setEncoding('utf8'); | ||
| socket.on('error', function(err) { | ||
| return sp.emit('error', err); | ||
| }); | ||
| socket.on('close', function(hadErr) { | ||
| return sp.emit('close', hadErr); | ||
| }); | ||
| socket.on('data', function(data) { | ||
| sp.emit('data', data); | ||
| return draw(data); | ||
| }); | ||
| readAccumulate = ''; | ||
| draw = function(sdata) { | ||
| var etx, rdata, results; | ||
| readAccumulate += sdata; | ||
| results = []; | ||
| while ((etx = readAccumulate.indexOf('\x03')) !== -1) { | ||
| rdata = readAccumulate.substr(0, etx); | ||
| readAccumulate = readAccumulate.substr(etx + 1); | ||
| results.push(handle(rdata)); | ||
| } | ||
| return results; | ||
| }; | ||
| return handle = function(sdata) { | ||
| return protocol.emit('command', sdata.trim(), sp); | ||
| }; | ||
| }); | ||
| return server.listen({ | ||
| port: protocol.config.listenPort | ||
| }); | ||
| }; | ||
| }).call(this); |
-161
| (function() { | ||
| var bindExit, findDeep, fs, isRunning, lock, lockfile, path, unbindExit; | ||
| lockfile = require('lockfile'); | ||
| fs = require('fs'); | ||
| path = require('path'); | ||
| isRunning = function(pid) { | ||
| var e; | ||
| try { | ||
| process.kill(pid, 0); | ||
| } catch (error) { | ||
| e = error; | ||
| return false; | ||
| } | ||
| return true; | ||
| }; | ||
| exports.lock = lock = function(name, status, configPath, configData, cb) { | ||
| var lockPath, pidPath, verification; | ||
| lockPath = path.join(path.dirname(configPath), name + ".lock"); | ||
| pidPath = path.join(path.dirname(configPath), name + ".pid"); | ||
| verification = { | ||
| lockPath: lockPath, | ||
| pidPath: pidPath | ||
| }; | ||
| return lockfile.lock(lockPath, {}, function(err) { | ||
| var fail; | ||
| if (err != null) { | ||
| fail = function() { | ||
| console.error("File " + lockPath + " already exists: is another instance running?", err); | ||
| return process.exit(1); | ||
| }; | ||
| return fs.exists(pidPath, function(exists) { | ||
| if (exists) { | ||
| return fs.readFile(pidPath, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| var pidobj; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| pidobj = JSON.parse(data); | ||
| if (!isRunning(pidobj.pid)) { | ||
| fs.unlinkSync(pidPath); | ||
| fs.unlinkSync(lockPath); | ||
| return lock(name, status, configPath, configData, cb); | ||
| } else { | ||
| return fail(); | ||
| } | ||
| }); | ||
| } else { | ||
| return fail(); | ||
| } | ||
| }); | ||
| } else { | ||
| return fs.writeFile(pidPath, JSON.stringify(status), { | ||
| encoding: 'utf8' | ||
| }, function(err) { | ||
| if (err) { | ||
| console.error("Could not write status file " + pidPath, err); | ||
| return process.exit(1); | ||
| } else { | ||
| verification.group = bindExit(function() { | ||
| fs.unlinkSync(pidPath); | ||
| return lockfile.unlockSync(lockPath); | ||
| }); | ||
| return cb(null, verification); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| }; | ||
| exports.unlock = function(verification) { | ||
| fs.unlinkSync(verification.pidPath); | ||
| lockfile.unlockSync(verification.lockPath); | ||
| return unbindExit(verification.group); | ||
| }; | ||
| exports.find = function(name, configPath, cb) { | ||
| return findDeep(name, configPath, false, cb); | ||
| }; | ||
| exports.findDeep = findDeep = function(name, configPath, erase, cb) { | ||
| var lockPath, pidPath; | ||
| lockPath = path.join(path.dirname(configPath), name + ".lock"); | ||
| pidPath = path.join(path.dirname(configPath), name + ".pid"); | ||
| return fs.exists(lockPath, function(exists) { | ||
| if (!exists) { | ||
| return cb(null, false); | ||
| } | ||
| return fs.exists(pidPath, function(exists) { | ||
| if (!exists) { | ||
| return cb(null, false); | ||
| } | ||
| return fs.readFile(pidPath, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| var pidobj; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| pidobj = JSON.parse(data); | ||
| if (!erase) { | ||
| return cb(null, true, pidobj); | ||
| } | ||
| if (isRunning(pidobj.pid)) { | ||
| return cb(null, true, pidobj); | ||
| } else { | ||
| fs.unlinkSync(pidPath); | ||
| fs.unlinkSync(lockPath); | ||
| return cb(null, false); | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| bindExit = function(fn) { | ||
| var group, handled; | ||
| handled = false; | ||
| group = { | ||
| fn: fn, | ||
| exit: function() { | ||
| if (!handled) { | ||
| fn(); | ||
| } | ||
| return handled = true; | ||
| }, | ||
| sigint: function() { | ||
| if (!handled) { | ||
| fn(); | ||
| } | ||
| handled = true; | ||
| return process.exit(130); | ||
| }, | ||
| sigterm: function() { | ||
| if (!handled) { | ||
| fn(); | ||
| } | ||
| handled = true; | ||
| return process.exit(131); | ||
| } | ||
| }; | ||
| process.on('exit', group.exit); | ||
| process.on('SIGINT', group.sigint); | ||
| process.on('SIGTERM', group.sigterm); | ||
| return group; | ||
| }; | ||
| unbindExit = function(group) { | ||
| process.removeListener('exit', group.exit); | ||
| process.removeListener('SIGINT', group.sigint); | ||
| process.removeListener('SIGTERM', group.sigterm); | ||
| return group; | ||
| }; | ||
| }).call(this); |
| (function() { | ||
| var _, async, config, createClientVitae, createServerVitae, crypto, events, forge, listen, net, open, path, ready, singleize; | ||
| _ = require('lodash'); | ||
| async = require('async'); | ||
| crypto = require('crypto'); | ||
| events = require('events'); | ||
| forge = require('node-forge'); | ||
| net = require('net'); | ||
| path = require('path'); | ||
| config = require('../config'); | ||
| createServerVitae = function(cfgData, socket) { | ||
| var ref, vitae; | ||
| vitae = _.cloneDeep((ref = cfgData.vitae) != null ? ref : {}); | ||
| _.assign(vitae, { | ||
| ip: socket.remoteAddress, | ||
| version: config.version().version | ||
| }); | ||
| return vitae; | ||
| }; | ||
| createClientVitae = function(data, socket) { | ||
| var ref, vitae; | ||
| vitae = _.cloneDeep((ref = data.vitae) != null ? ref : {}); | ||
| _.assign(vitae, { | ||
| ip: socket.remoteAddress, | ||
| since: new Date().toString() | ||
| }); | ||
| return vitae; | ||
| }; | ||
| singleize = function(vals) { | ||
| if (vals.length === 1) { | ||
| return vals[0]; | ||
| } else { | ||
| return vals; | ||
| } | ||
| }; | ||
| exports.create = function() { | ||
| var protocol; | ||
| protocol = new events.EventEmitter(); | ||
| protocol.listen = function(lt) { | ||
| return listen(protocol, lt); | ||
| }; | ||
| return protocol; | ||
| }; | ||
| listen = function(protocol, lt) { | ||
| protocol.status = {}; | ||
| protocol.config = lt.config; | ||
| protocol.backchannels = {}; | ||
| return open(protocol); | ||
| }; | ||
| open = function(protocol) { | ||
| var ended, server; | ||
| _.defaults(protocol.config, { | ||
| backPort: 3015 | ||
| }); | ||
| ended = false; | ||
| protocol.end = function() {}; | ||
| server = new net.Server(); | ||
| server.on('error', function(err) { | ||
| return protocol.emit('error', err); | ||
| }); | ||
| server.on('close', function() { | ||
| return protocol.emit('close'); | ||
| }); | ||
| server.on('listening', function() { | ||
| protocol.end = function() { | ||
| if (!ended) { | ||
| ended = true; | ||
| server.close(); | ||
| return protocol.emit('shutdown'); | ||
| } | ||
| }; | ||
| return protocol.emit('listening', server.address()); | ||
| }); | ||
| server.on('connection', function(socket) { | ||
| var shutdownFn, sp, spended; | ||
| shutdownFn = function() { | ||
| return socket.end(); | ||
| }; | ||
| protocol.on('shutdown', shutdownFn); | ||
| sp = new events.EventEmitter(); | ||
| spended = false; | ||
| _.assign(sp, { | ||
| reply: function(data) { | ||
| return socket.write(JSON.stringify(data) + '\x03'); | ||
| }, | ||
| end: function() { | ||
| if (!spended) { | ||
| spended = true; | ||
| return socket.end(); | ||
| } | ||
| } | ||
| }); | ||
| socket.setEncoding('utf8'); | ||
| socket.on('error', function(err) { | ||
| return sp.emit('error', err); | ||
| }); | ||
| socket.on('close', function(hadErr) { | ||
| return protocol.removeListener('shutdown', shutdownFn); | ||
| }); | ||
| socket.on('data', function(data) { | ||
| return sp.emit('data', data); | ||
| }); | ||
| ready(protocol, socket, sp); | ||
| return protocol.emit('connection', sp); | ||
| }); | ||
| return server.listen({ | ||
| port: protocol.config.backPort | ||
| }); | ||
| }; | ||
| ready = function(protocol, socket, sp) { | ||
| var bindPort, cfgData, closing, draw, handle, noted, patches, rawprompt, readAccumulate, registeredId, registry, responses; | ||
| cfgData = protocol.config; | ||
| registry = cfgData.registry; | ||
| patches = cfgData.patches; | ||
| rawprompt = null; | ||
| registeredId = null; | ||
| bindPort = null; | ||
| closing = false; | ||
| noted = []; | ||
| readAccumulate = ''; | ||
| draw = function(sdata) { | ||
| var etx, rdata, results; | ||
| readAccumulate += sdata; | ||
| results = []; | ||
| while ((etx = readAccumulate.indexOf('\x03')) !== -1) { | ||
| rdata = readAccumulate.substr(0, etx); | ||
| readAccumulate = readAccumulate.substr(etx + 1); | ||
| results.push(handle(rdata)); | ||
| } | ||
| return results; | ||
| }; | ||
| socket.on('data', draw); | ||
| socket.on('close', function(hadErr) { | ||
| var i, len, note, ref, ref1; | ||
| closing = true; | ||
| for (i = 0, len = noted.length; i < len; i++) { | ||
| note = noted[i]; | ||
| if (!((ref = (ref1 = sp.stale) != null ? ref1[note] : void 0) != null ? ref : false)) { | ||
| delete protocol.backchannels[note]; | ||
| registry.disconnected(note); | ||
| } | ||
| } | ||
| sp.emit('close', hadErr, noted); | ||
| return noted = []; | ||
| }); | ||
| responses = { | ||
| ping1: function(data) { | ||
| return sp.reply({ | ||
| type: 'didPing1' | ||
| }); | ||
| }, | ||
| check1: function(data) { | ||
| var ref; | ||
| return patches.check((ref = data.vitae) != null ? ref : {}, function(err, patch) { | ||
| if (err != null) { | ||
| return sp.reply({ | ||
| type: 'error', | ||
| error: err | ||
| }); | ||
| } | ||
| return sp.reply({ | ||
| type: 'didCheck1', | ||
| patch: patch | ||
| }); | ||
| }); | ||
| }, | ||
| upgrade1: function(data) { | ||
| return patches.retrieve(data.version, data.id, function(err, patch) { | ||
| var giver; | ||
| if (err != null) { | ||
| return sp.reply({ | ||
| type: 'error', | ||
| error: err | ||
| }); | ||
| } | ||
| giver = patches.readable(patch, function() { | ||
| return socket.setEncoding('utf8'); | ||
| }); | ||
| socket.setEncoding(null); | ||
| delete socket._writableState.encoder; | ||
| return giver.pipe(socket); | ||
| }); | ||
| }, | ||
| handshake1: function(data) { | ||
| var replacement; | ||
| replacement = []; | ||
| registeredId = data.registeredId; | ||
| if (!(_.isArray(registeredId))) { | ||
| registeredId = [registeredId]; | ||
| } | ||
| if (data.handshakeVersion !== 1) { | ||
| return sp.reply({ | ||
| type: 'error', | ||
| error: "Bad handshake version " | ||
| }); | ||
| } | ||
| return registry.sameKey(registeredId, function(err, sk) { | ||
| if (err != null) { | ||
| return sp.reply({ | ||
| type: 'error', | ||
| error: "Couldn't detect keys in registry: " + err | ||
| }); | ||
| } | ||
| if (!sk) { | ||
| return sp.reply({ | ||
| type: 'error', | ||
| error: "Registered IDs " + registeredId + " use different keys." | ||
| }); | ||
| } | ||
| replacement = registry.isAvailable(registeredId); | ||
| if (!(_.isEmpty(replacement))) { | ||
| sp.emit('replacement', replacement); | ||
| } | ||
| registry.connecting(registeredId); | ||
| return registry.find(registeredId, function(err, key, ports) { | ||
| var cryptText; | ||
| if (err != null) { | ||
| return sp.reply({ | ||
| type: 'error', | ||
| error: "No registration found for \"" + registeredId + "\"" | ||
| }); | ||
| } | ||
| bindPort = ports; | ||
| rawprompt = crypto.randomBytes(50); | ||
| cryptText = crypto.publicEncrypt(key, rawprompt); | ||
| return sp.reply({ | ||
| type: 'shakehand1', | ||
| replacement: replacement, | ||
| prompt: cryptText.toString('base64') | ||
| }); | ||
| }); | ||
| }); | ||
| }, | ||
| handshake2: function(data) { | ||
| var vitae; | ||
| if ((rawprompt == null) || !new Buffer(data.response, 'base64').equals(rawprompt)) { | ||
| return sp.reply({ | ||
| type: 'error', | ||
| error: "Failed identity verification" | ||
| }); | ||
| } | ||
| vitae = createServerVitae(cfgData, socket); | ||
| return sp.reply({ | ||
| type: 'shakehand2', | ||
| bindPort: singleize(bindPort), | ||
| vitae: vitae | ||
| }); | ||
| }, | ||
| handshake3: function(data) { | ||
| var vitae; | ||
| vitae = createClientVitae(data, socket); | ||
| return async.each(registeredId, function(regId, cb) { | ||
| var afterClose, closeDone, complete, oldsp; | ||
| complete = function() { | ||
| if (closing) { | ||
| return; | ||
| } | ||
| registry.connected(regId); | ||
| registry.setVitae(regId, vitae); | ||
| protocol.backchannels[regId] = sp; | ||
| noted.push(regId); | ||
| return cb(null); | ||
| }; | ||
| if ((oldsp = protocol.backchannels[regId]) != null) { | ||
| closeDone = false; | ||
| afterClose = function() { | ||
| if (closeDone) { | ||
| return; | ||
| } | ||
| closeDone = true; | ||
| if (oldsp.stale == null) { | ||
| oldsp.stale = {}; | ||
| } | ||
| oldsp.stale[regId] = true; | ||
| return complete(); | ||
| }; | ||
| oldsp.once('close', afterClose); | ||
| setTimeout(afterClose, 1000); | ||
| return oldsp.end(); | ||
| } else { | ||
| return complete(); | ||
| } | ||
| }, function(err) { | ||
| return sp.emit('handshake', sp, registeredId); | ||
| }); | ||
| }, | ||
| register1: function(data) { | ||
| return registry.register(data.publicKey, data.preferred, function(err, regId, entry) { | ||
| if (err != null) { | ||
| return sp.reply({ | ||
| type: 'error', | ||
| error: "Failed public key." + err.toString() | ||
| }); | ||
| } | ||
| return sp.reply({ | ||
| type: 'didRegister1', | ||
| registeredId: regId, | ||
| registeredPort: entry.port | ||
| }); | ||
| }); | ||
| }, | ||
| register2: function(data) { | ||
| return sp.emit('registered'); | ||
| }, | ||
| unregister1: function(data) { | ||
| if (data.handshakeVersion !== 1) { | ||
| return sp.reply({ | ||
| type: 'error', | ||
| error: "Bad handshake version " | ||
| }); | ||
| } | ||
| registeredId = data.registeredId; | ||
| if (!(_.isArray(registeredId))) { | ||
| registeredId = [registeredId]; | ||
| } | ||
| return registry.find(registeredId, function(err, key, ports) { | ||
| var cryptText; | ||
| if (err != null) { | ||
| return sp.reply({ | ||
| type: 'error', | ||
| error: "No registration found for \"" + registeredId + "\": " + err | ||
| }); | ||
| } | ||
| rawprompt = crypto.randomBytes(50); | ||
| cryptText = crypto.publicEncrypt(key, rawprompt); | ||
| return sp.reply({ | ||
| type: 'didUnregister1', | ||
| prompt: cryptText.toString('base64') | ||
| }); | ||
| }); | ||
| }, | ||
| unregister2: function(data) { | ||
| if ((rawprompt == null) || !new Buffer(data.response, 'base64').equals(rawprompt)) { | ||
| return sp.reply({ | ||
| type: 'error', | ||
| error: "Failed identity verification" | ||
| }); | ||
| } | ||
| return registry.unregister(registeredId, function(err) { | ||
| if (err != null) { | ||
| return sp.reply({ | ||
| type: 'error', | ||
| error: "Unregistration failed for \"" + registeredId + "\": " + err | ||
| }); | ||
| } | ||
| return sp.reply({ | ||
| type: 'didUnregister2' | ||
| }); | ||
| }); | ||
| }, | ||
| unregister3: function(data) { | ||
| return sp.emit('unregistered'); | ||
| } | ||
| }; | ||
| return handle = function(sdata) { | ||
| var data, e, ref, respf; | ||
| try { | ||
| data = JSON.parse(sdata); | ||
| } catch (error) { | ||
| e = error; | ||
| return protocol.emit('error', e); | ||
| } | ||
| respf = (ref = responses[data.type]) != null ? ref : function(data) { | ||
| return sp.reply({ | ||
| type: 'error', | ||
| error: "No response to type \"" + data.type + "\"" | ||
| }); | ||
| }; | ||
| return respf(data); | ||
| }; | ||
| }; | ||
| }).call(this); |
| #!/usr/bin/env node | ||
| (function() { | ||
| var _, async, fs, rclient, redis, regFn, regUrl, warn, | ||
| slice = [].slice; | ||
| _ = require('lodash'); | ||
| redis = require('redis'); | ||
| async = require('async'); | ||
| fs = require('fs'); | ||
| if (process.argv.length < 3) { | ||
| return console.warn("Usage: reg-convert <registryName>"); | ||
| } | ||
| regFn = process.argv[2]; | ||
| regUrl = 'redis://localhost:6379'; | ||
| rclient = redis.createClient(regUrl); | ||
| warn = function() { | ||
| var args; | ||
| args = 1 <= arguments.length ? slice.call(arguments, 0) : []; | ||
| console.warn.apply(console, args); | ||
| return rclient.quit(); | ||
| }; | ||
| fs.readFile(regFn, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| var ex, max, maxPort, ref, ref1, registry; | ||
| if (err != null) { | ||
| return warn("Unable to read " + regFn + ": " + err); | ||
| } | ||
| try { | ||
| registry = JSON.parse(data); | ||
| } catch (error) { | ||
| ex = error; | ||
| if (err != null) { | ||
| return warn("Unable to parse " + regFn + ": " + err); | ||
| } | ||
| } | ||
| max = Math.max(4000, (ref = registry.maxRegisteredId) != null ? ref : 0); | ||
| maxPort = Math.max(14000, (ref1 = registry.maxRegisteredPort) != null ? ref1 : 0); | ||
| return async.eachSeries(Object.keys(registry.registeredIds), function(regId, cb) { | ||
| var key, port, ref2, vitae; | ||
| ref2 = registry.registeredIds[regId], port = ref2.port, key = ref2.key, vitae = ref2.vitae; | ||
| if (/^\d+$/.test(regId)) { | ||
| max = Math.max(max, parseInt(regId)); | ||
| } | ||
| return async.series([ | ||
| function(cb) { | ||
| if (vitae != null) { | ||
| return rclient.set("g2c/ids/" + regId + "/vitae", JSON.stringify(vitae), cb); | ||
| } else { | ||
| return cb(); | ||
| } | ||
| }, function(cb) { | ||
| return rclient.set("g2c/ids/" + regId + "/key", key, cb); | ||
| }, function(cb) { | ||
| return rclient.set("g2c/ids/" + regId + "/port", port, cb); | ||
| }, function(cb) { | ||
| return rclient.set("g2c/ports/" + port, regId, cb); | ||
| }, function(cb) { | ||
| return rclient.sadd("g2c/allids", regId, cb); | ||
| } | ||
| ], cb); | ||
| }, function(err) { | ||
| return async.series([ | ||
| function(cb) { | ||
| return rclient.set("g2c/max/port", "" + maxPort, cb); | ||
| }, function(cb) { | ||
| return rclient.set("g2c/max/id", "" + max, cb); | ||
| } | ||
| ], function(err) { | ||
| console.log("Done"); | ||
| return rclient.quit(); | ||
| }); | ||
| }); | ||
| }); | ||
| }).call(this); |
| (function() { | ||
| var _, async, createManager, forge, fs, init, mkdirp, patchAdjust, patchCompare, patchEquals, patchFind, patchGet, patchIntersect, patchPathsFrom, path; | ||
| _ = require('lodash'); | ||
| async = require('async'); | ||
| fs = require('fs'); | ||
| path = require('path'); | ||
| mkdirp = require('mkdirp'); | ||
| forge = require('node-forge'); | ||
| patchPathsFrom = function(configPath) { | ||
| var dpath, ipath; | ||
| dpath = path.join(path.dirname(configPath), 'patches'); | ||
| ipath = path.join(dpath, 'index.json'); | ||
| return [dpath, ipath]; | ||
| }; | ||
| exports.init = init = function(configPath, configData, cb) { | ||
| var indexPath, patchPath, ref; | ||
| ref = patchPathsFrom(configPath), patchPath = ref[0], indexPath = ref[1]; | ||
| return mkdirp(patchPath, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return fs.exists(indexPath, function(exists) { | ||
| var data; | ||
| if (exists) { | ||
| return cb(null, false, indexPath); | ||
| } else { | ||
| data = { | ||
| versions: {} | ||
| }; | ||
| return fs.writeFile(indexPath, JSON.stringify(data, null, 2), { | ||
| encoding: 'utf8' | ||
| }, function(err) { | ||
| return cb(err, true, indexPath); | ||
| }); | ||
| } | ||
| }); | ||
| }); | ||
| }; | ||
| exports.manage = function(configPath, configData, cb) { | ||
| return init(configPath, configData, function(err) { | ||
| var indexPath, patchPath, ref; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| ref = patchPathsFrom(configPath), patchPath = ref[0], indexPath = ref[1]; | ||
| return fs.readFile(indexPath, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| data = JSON.parse(data); | ||
| return cb(null, createManager(indexPath, data)); | ||
| }); | ||
| }); | ||
| }; | ||
| createManager = function(ipath, data) { | ||
| var check, readable, retrieve, verify, verifyAll, write; | ||
| write = function(cb) { | ||
| return fs.writeFile(ipath, JSON.stringify(data, null, 2), { | ||
| encoding: 'utf8' | ||
| }, cb); | ||
| }; | ||
| verifyAll = function(cb) { | ||
| return async.each(_.toPairs(data.versions), (function(arg, cb) { | ||
| var k, v; | ||
| k = arg[0], v = arg[1]; | ||
| return verify(k, _.cloneDeep(v), cb); | ||
| }), cb); | ||
| }; | ||
| verify = function(version, vdata, cb) { | ||
| var ambigs, confls, found, i, isect, j, k, l, m, n, ref, ref1, ref2, ref3, ref4; | ||
| vdata.sort(patchCompare); | ||
| ambigs = []; | ||
| confls = []; | ||
| for (i = l = 0, ref = vdata.length; 0 <= ref ? l < ref : l > ref; i = 0 <= ref ? ++l : --l) { | ||
| for (j = m = ref1 = i + 1, ref2 = vdata.length; ref1 <= ref2 ? m < ref2 : m > ref2; j = ref1 <= ref2 ? ++m : --m) { | ||
| if (patchEquals(vdata[i], vdata[j])) { | ||
| if (!(_.some(confls, function(a) { | ||
| return patchEquals({ | ||
| tags: a | ||
| }, vdata[i]); | ||
| }))) { | ||
| confls.push(vdata[i].tags); | ||
| } | ||
| continue; | ||
| } | ||
| if (patchCompare(vdata[i], vdata[j]) < 0) { | ||
| continue; | ||
| } | ||
| isect = patchIntersect(vdata[i], vdata[j]); | ||
| if (isect.empty) { | ||
| continue; | ||
| } | ||
| found = false; | ||
| for (k = n = ref3 = j + 1, ref4 = vdata.length; ref3 <= ref4 ? n < ref4 : n > ref4; k = ref3 <= ref4 ? ++n : --n) { | ||
| if (patchEquals(isect, vdata[k])) { | ||
| found = true; | ||
| break; | ||
| } | ||
| } | ||
| if (found) { | ||
| continue; | ||
| } | ||
| if (!(_.some(ambigs, function(a) { | ||
| return patchEquals({ | ||
| tags: a | ||
| }, isect); | ||
| }))) { | ||
| ambigs.push(isect.tags); | ||
| } | ||
| } | ||
| } | ||
| if (ambigs.length === 0 && confls.length === 0) { | ||
| return cb(null); | ||
| } else if (ambigs.length === 0) { | ||
| return cb(new Error("conflicts in " + version + ": " + (JSON.stringify(confls)))); | ||
| } else if (confls.length === 0) { | ||
| return cb(new Error("ambiguities in " + version + ": " + (JSON.stringify(ambigs)))); | ||
| } else { | ||
| return cb(new Error("in " + version + " ambiguities: " + (JSON.stringify(ambigs)) + ", conflicts: " + (JSON.stringify(confls)))); | ||
| } | ||
| }; | ||
| check = function(vitae, cb) { | ||
| var ref, vdata; | ||
| vdata = (ref = data.versions[vitae.version]) != null ? ref : []; | ||
| return patchFind(vdata, vitae, function(err, patch) { | ||
| var result; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if (patch != null) { | ||
| result = _.cloneDeep(patch); | ||
| result.version = vitae.version; | ||
| result.update = true; | ||
| } else { | ||
| result = { | ||
| update: false | ||
| }; | ||
| } | ||
| return cb(null, result); | ||
| }); | ||
| }; | ||
| retrieve = function(version, id, cb) { | ||
| return patchGet(data.versions[version], id, cb); | ||
| }; | ||
| readable = function(patch, done) { | ||
| var read; | ||
| read = fs.createReadStream('/Users/charlieozinga/tmp/IN_PATCH'); | ||
| read.once('end', done); | ||
| return read; | ||
| }; | ||
| return { | ||
| check: check, | ||
| verifyAll: verifyAll, | ||
| verify: verify, | ||
| retrieve: retrieve, | ||
| readable: readable | ||
| }; | ||
| }; | ||
| patchCompare = function(p1, p2) { | ||
| var p1only, p2only; | ||
| p1only = _.difference(_.keys(p1.tags), _.keys(p2.tags)); | ||
| p2only = _.difference(_.keys(p2.tags), _.keys(p1.tags)); | ||
| if (_.isEmpty(p1only)) { | ||
| if (_.isEmpty(p2only)) { | ||
| return 0; | ||
| } else { | ||
| return -1; | ||
| } | ||
| } else if (_.isEmpty(p2only)) { | ||
| return 1; | ||
| } else { | ||
| return 0; | ||
| } | ||
| }; | ||
| patchEquals = function(p1, p2) { | ||
| var k, l, len, p1only, p2only, ref; | ||
| p1only = _.difference(_.keys(p1.tags), _.keys(p2.tags)); | ||
| p2only = _.difference(_.keys(p2.tags), _.keys(p1.tags)); | ||
| if (!_.isEmpty(p1only) || !_.isEmpty(p2only)) { | ||
| return false; | ||
| } | ||
| ref = _.keys(p1.tags); | ||
| for (l = 0, len = ref.length; l < len; l++) { | ||
| k = ref[l]; | ||
| if (p1.tags[k] !== p2.tags[k]) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| }; | ||
| patchIntersect = function(p1, p2) { | ||
| var isect, k, l, len, len1, len2, m, n, p1only, p2only, pboth; | ||
| p1only = _.difference(_.keys(p1.tags), _.keys(p2.tags)); | ||
| p2only = _.difference(_.keys(p2.tags), _.keys(p1.tags)); | ||
| pboth = _.intersection(_.keys(p1.tags), _.keys(p2.tags)); | ||
| isect = { | ||
| empty: false, | ||
| tags: {} | ||
| }; | ||
| for (l = 0, len = pboth.length; l < len; l++) { | ||
| k = pboth[l]; | ||
| if (p1.tags[k] !== p2.tags[k]) { | ||
| return { | ||
| empty: true | ||
| }; | ||
| } | ||
| isect.tags[k] = p1.tags[k]; | ||
| } | ||
| for (m = 0, len1 = p1only.length; m < len1; m++) { | ||
| k = p1only[m]; | ||
| isect.tags[k] = p1.tags[k]; | ||
| } | ||
| for (n = 0, len2 = p2only.length; n < len2; n++) { | ||
| k = p2only[n]; | ||
| isect.tags[k] = p2.tags[k]; | ||
| } | ||
| return isect; | ||
| }; | ||
| patchFind = function(vdata, vitae, cb) { | ||
| var best, candidates; | ||
| candidates = _.filter(vdata, function(p) { | ||
| var both, k, l, len, ponly; | ||
| ponly = _.difference(_.keys(p.tags), _.keys(vitae)); | ||
| if (!(_.isEmpty(ponly))) { | ||
| return false; | ||
| } | ||
| both = _.intersection(_.keys(p.tags), _.keys(vitae)); | ||
| for (l = 0, len = both.length; l < len; l++) { | ||
| k = both[l]; | ||
| if (p.tags[k] !== vitae[k]) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| }); | ||
| best = _.maxBy(candidates, function(c) { | ||
| return _.keys(c.tags).length; | ||
| }); | ||
| return patchAdjust(best, cb); | ||
| }; | ||
| patchGet = function(vdata, id, cb) { | ||
| var best; | ||
| best = _.find(vdata, function(p) { | ||
| return p.id === id; | ||
| }); | ||
| if (best != null) { | ||
| return patchAdjust(best, cb); | ||
| } else { | ||
| return cb(new Error("Couldn't retrieve patch " + id)); | ||
| } | ||
| }; | ||
| patchAdjust = function(patch, cb) { | ||
| if (patch == null) { | ||
| return cb(null); | ||
| } | ||
| return fs.stat('/Users/charlieozinga/tmp/IN_PATCH', function(err, stats) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| patch = _.cloneDeep(patch); | ||
| patch.size = stats.size; | ||
| return cb(null, patch); | ||
| }); | ||
| }; | ||
| }).call(this); |
| #!/usr/bin/env node | ||
| (function() { | ||
| var _, backtalk, childs, commands, config, forge, fs, getopt, lock, main, net, patches, path, registry, say, util, warn; | ||
| _ = require('lodash'); | ||
| childs = require('child_process'); | ||
| getopt = require('node-getopt'); | ||
| forge = require('node-forge'); | ||
| fs = require('fs'); | ||
| net = require('net'); | ||
| path = require('path'); | ||
| config = require('../config'); | ||
| lock = require('../lock'); | ||
| backtalk = require('./backtalk'); | ||
| registry = require('./registry_redis'); | ||
| patches = require('./patches'); | ||
| util = require('./util'); | ||
| say = console.log; | ||
| warn = console.warn; | ||
| main = function() { | ||
| var opt, opts, optsrc, ref, ref1, task, usage; | ||
| usage = function() { | ||
| say("Usage: petit-server <command> [OPTIONS]"); | ||
| say("Known commands are:"); | ||
| say(" " + _.sortBy(_.keys(commands)).join(', ')); | ||
| return process.exit(1); | ||
| }; | ||
| if (process.argv.length < 3 || ((ref = process.argv[2]) === '-h' || ref === '--help')) { | ||
| usage(); | ||
| } | ||
| task = commands[process.argv[2]]; | ||
| if (task == null) { | ||
| say("Unknown command \"" + process.argv[2] + "\""); | ||
| usage(); | ||
| } | ||
| optsrc = (ref1 = task.opts) != null ? ref1 : []; | ||
| optsrc.push.apply(optsrc, [['c', 'config=ARG', 'use config file'], ['v', 'verbose', 'print debug output'], ['h', 'help', 'display this help']]); | ||
| opt = getopt.create(optsrc); | ||
| opt.setHelp("Usage: petit-server " + process.argv[2] + " [OPTION]\n\n[[OPTIONS]]"); | ||
| opts = opt.bindHelp().parseSystem(); | ||
| return task.run(opts); | ||
| }; | ||
| commands = { | ||
| init: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| return config.createConfig(opts.options.config, function(err, made, path) { | ||
| if (err != null) { | ||
| return warn("Can't create config file.", err); | ||
| } | ||
| if (made) { | ||
| say("Created configuration file \"" + path + "\"."); | ||
| return config.readConfig(path, function(err, configPath, configData) { | ||
| if (err != null) { | ||
| return warn("Can't read config \"" + configPath + "\".", err); | ||
| } | ||
| return registry.init(configPath, configData, function(err, made, path) { | ||
| if (err != null) { | ||
| return warn("Can't create registry.", err); | ||
| } | ||
| say("Created registry file \"" + path + "\"."); | ||
| return patches.init(configPath, configData, function(err, made, path) { | ||
| if (err != null) { | ||
| return warn("Can't create patches.", err); | ||
| } | ||
| return say("Created patches file \"" + path + "\"."); | ||
| }); | ||
| }); | ||
| }); | ||
| } else { | ||
| return say("Configuration file \"" + path + "\" already exists."); | ||
| } | ||
| }); | ||
| } | ||
| }, | ||
| start: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var configPath; | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return fs.exists(configPath, function(exists) { | ||
| if (!exists) { | ||
| return warn("No config at \"" + configPath + "\": use \"init\"."); | ||
| } | ||
| return config.readConfig(configPath, function(err, configPath, data) { | ||
| var onExists, regPath, regUrl; | ||
| if (err != null) { | ||
| return warn("Couldn't read config \"" + configPath + "\".", err); | ||
| } | ||
| regPath = data.server.registryfile; | ||
| regUrl = data.server.registryUrl; | ||
| if ((regPath == null) && (regUrl == null)) { | ||
| return warn("No registry file in config"); | ||
| } | ||
| onExists = function(exists) { | ||
| var exe; | ||
| if (!exists) { | ||
| say("No registry. Use \"init\" first."); | ||
| process.exit(101); | ||
| } | ||
| exe = path.join(path.dirname(fs.realpathSync(__filename)), 'petit-serverd-bin') + path.extname(__filename); | ||
| return lock.findDeep('server', configPath, true, function(err, exists, data) { | ||
| var ch, checkgo; | ||
| if (err != null) { | ||
| return warn(err); | ||
| } | ||
| if (exists) { | ||
| say("Server is already running with pid " + data.pid + "."); | ||
| return process.exit(101); | ||
| } | ||
| ch = childs.spawn(exe, ['-c', configPath], { | ||
| detached: true, | ||
| stdio: 'ignore' | ||
| }); | ||
| ch.once('error', function(err) { | ||
| return warn("Unable to start server.", err); | ||
| }); | ||
| ch.unref(); | ||
| checkgo = function(bads) { | ||
| return function() { | ||
| return lock.find('server', configPath, function(err, exists, data) { | ||
| if (err != null) { | ||
| return warn(err); | ||
| } | ||
| if (exists) { | ||
| return say("Server started with pid " + data.pid + "."); | ||
| } else if (bads < 20) { | ||
| return setTimeout(checkgo(bads + 1), 300); | ||
| } else { | ||
| return say("Unable to start server."); | ||
| } | ||
| }); | ||
| }; | ||
| }; | ||
| return setTimeout(checkgo(0), 300); | ||
| }); | ||
| }; | ||
| if (regUrl == null) { | ||
| return fs.exists(regPath, onExists); | ||
| } else { | ||
| return onExists(true); | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
| }, | ||
| stop: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var configPath, isKilled, waitKilled; | ||
| configPath = config.pickConfig(opts.options.config); | ||
| lock.find('server', configPath, function(err, exists, data) { | ||
| var conn; | ||
| if (err != null) { | ||
| return warn(err); | ||
| } | ||
| if (!exists) { | ||
| say("Server is not running."); | ||
| return process.exit(101); | ||
| } | ||
| conn = new net.Socket(); | ||
| conn.setEncoding('utf8'); | ||
| conn.on('close', function() { | ||
| return waitKilled(data.pid, function(killed) { | ||
| if (killed) { | ||
| return say("Server is stopped."); | ||
| } | ||
| process.kill(data.pid, 'SIGINT'); | ||
| return waitKilled(data.pid, function(killed) { | ||
| if (killed) { | ||
| return say("Server is stopped with SIGINT."); | ||
| } else { | ||
| return say("Unable to stop server pid " + data.pid); | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| return conn.connect(data.listen, 'localhost', function() { | ||
| return conn.end('STOP\x03'); | ||
| }); | ||
| }); | ||
| waitKilled = function(pid, cb) { | ||
| var maybeDone, repeater, total; | ||
| if (isKilled(pid)) { | ||
| return cb(true); | ||
| } | ||
| total = 0; | ||
| maybeDone = function() { | ||
| if (isKilled(pid)) { | ||
| clearInterval(repeater); | ||
| return cb(true); | ||
| } else if (total < 10) { | ||
| return ++total; | ||
| } else { | ||
| clearInterval(repeater); | ||
| return cb(false); | ||
| } | ||
| }; | ||
| return repeater = setInterval(maybeDone, 100); | ||
| }; | ||
| return isKilled = function(pid) { | ||
| var e; | ||
| try { | ||
| process.kill(pid, 0); | ||
| } catch (error) { | ||
| e = error; | ||
| return true; | ||
| } | ||
| return false; | ||
| }; | ||
| } | ||
| }, | ||
| status: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var configPath; | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return lock.find('server', configPath, function(err, exists, piddata) { | ||
| var buf, conn; | ||
| if (err != null) { | ||
| return warn(err); | ||
| } | ||
| if (!exists) { | ||
| say("Server is not running."); | ||
| return process.exit(101); | ||
| } | ||
| conn = new net.Socket(); | ||
| conn.setEncoding('utf8'); | ||
| buf = ""; | ||
| conn.on('data', function(data) { | ||
| var alias, cconn, e, fp, i, j, len, len1, obj, others, out, ref, ref1, ref10, ref11, ref12, ref13, ref14, ref15, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, regId; | ||
| try { | ||
| buf += data; | ||
| obj = JSON.parse(buf); | ||
| } catch (error) { | ||
| e = error; | ||
| say("...retrieving data..."); | ||
| return; | ||
| } | ||
| say("Server has status \"" + obj.status + "\" on pid " + piddata.pid + "."); | ||
| ref = obj.connections; | ||
| for (i = 0, len = ref.length; i < len; i++) { | ||
| cconn = ref[i]; | ||
| regId = cconn.registeredId; | ||
| if (opts.argv.length > 1 && regId !== opts.argv[1]) { | ||
| continue; | ||
| } | ||
| fp = (ref1 = cconn.fingerprint) != null ? ref1 : ''; | ||
| if (cconn.connected === true) { | ||
| say(" + " + regId + " " + ((ref2 = cconn.port) != null ? ref2 : ' ') + " " + fp); | ||
| } else if (cconn.connected === false) { | ||
| say(" - " + regId + " " + ((ref3 = cconn.port) != null ? ref3 : ' ') + " " + fp); | ||
| } else { | ||
| say(" / " + regId + " " + ((ref4 = cconn.port) != null ? ref4 : ' ') + " " + fp); | ||
| } | ||
| if (opts.argv.length > 1) { | ||
| if (((ref5 = cconn.vitae) != null ? ref5.ip : void 0) != null) { | ||
| if (((ref6 = cconn.vitae) != null ? ref6.hostname : void 0) != null) { | ||
| say(" \"" + cconn.vitae.hostname + "\" @ " + cconn.vitae.ip); | ||
| } else { | ||
| say(" @ " + cconn.vitae.ip); | ||
| } | ||
| } else if (((ref7 = cconn.vitae) != null ? ref7.hostname : void 0) != null) { | ||
| say(" \"" + cconn.vitae.hostname + "\""); | ||
| } | ||
| if (((ref8 = cconn.vitae) != null ? ref8.since : void 0) != null) { | ||
| say(" " + cconn.vitae.since); | ||
| } | ||
| if (((ref9 = cconn.vitae) != null ? ref9.platform : void 0) != null) { | ||
| say(" " + ((ref10 = cconn.vitae.version) != null ? ref10 : 'unknown_version') + " (" + ((ref11 = cconn.vitae.brand) != null ? ref11 : 'no_brand') + ") on " + cconn.vitae.platform + " " + ((ref12 = cconn.vitae.arch) != null ? ref12 : 'unknown_arch') + " " + ((ref13 = cconn.vitae.release) != null ? ref13 : 'unknown_release')); | ||
| } | ||
| others = _.omit(cconn.vitae, ['ip', 'hostname', 'since', 'platform', 'arch', 'brand', 'release', 'version']); | ||
| if (!(_.isEmpty(others))) { | ||
| out = _.map(_.toPairs(others), function(arg) { | ||
| var k, v; | ||
| k = arg[0], v = arg[1]; | ||
| return k + "=" + v; | ||
| }).join(", "); | ||
| say(" " + out); | ||
| } | ||
| ref15 = (ref14 = cconn.aliases) != null ? ref14 : []; | ||
| for (j = 0, len1 = ref15.length; j < len1; j++) { | ||
| alias = ref15[j]; | ||
| say(" = " + alias); | ||
| } | ||
| } | ||
| } | ||
| return conn.end(); | ||
| }); | ||
| if (opts.argv.length > 1) { | ||
| return conn.connect(piddata.listen, 'localhost', function() { | ||
| return conn.write("STATUS " + opts.argv[1] + "\x03"); | ||
| }); | ||
| } else { | ||
| return conn.connect(piddata.listen, 'localhost', function() { | ||
| return conn.write('STATUS\x03'); | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| }, | ||
| addalias: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var alias, configPath, regId; | ||
| if (opts.argv.length !== 3) { | ||
| say("Usage: petit-server addalias [OPTIONS] id alias"); | ||
| return process.exit(1); | ||
| } | ||
| regId = opts.argv[1].trim(); | ||
| alias = opts.argv[2].trim(); | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return lock.find('server', configPath, function(err, exists, piddata) { | ||
| var conn; | ||
| if (err != null) { | ||
| return warn(err); | ||
| } | ||
| if (!exists) { | ||
| say("Server is not running."); | ||
| return process.exit(101); | ||
| } | ||
| conn = new net.Socket(); | ||
| conn.setEncoding('utf8'); | ||
| conn.on('data', function(data) { | ||
| var obj; | ||
| obj = JSON.parse(data); | ||
| if (obj.success) { | ||
| say("Alias added."); | ||
| } else { | ||
| say("Alias not added:", obj.error); | ||
| } | ||
| return conn.end(); | ||
| }); | ||
| return conn.connect(piddata.listen, 'localhost', function() { | ||
| return conn.write("ADDALIAS " + regId + " " + alias + "\x03"); | ||
| }); | ||
| }); | ||
| } | ||
| }, | ||
| removealias: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var alias, configPath, regId; | ||
| if (opts.argv.length !== 3) { | ||
| say("Usage: petit-server addalias [OPTIONS] id alias"); | ||
| return process.exit(1); | ||
| } | ||
| regId = opts.argv[1].trim(); | ||
| alias = opts.argv[2].trim(); | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return lock.find('server', configPath, function(err, exists, piddata) { | ||
| var conn; | ||
| if (err != null) { | ||
| return warn(err); | ||
| } | ||
| if (!exists) { | ||
| say("Server is not running."); | ||
| return process.exit(101); | ||
| } | ||
| conn = new net.Socket(); | ||
| conn.setEncoding('utf8'); | ||
| conn.on('data', function(data) { | ||
| var obj; | ||
| obj = JSON.parse(data); | ||
| if (obj.success) { | ||
| say("Alias deleted."); | ||
| } else { | ||
| say("Alias not deleted:", obj.error); | ||
| } | ||
| return conn.end(); | ||
| }); | ||
| return conn.connect(piddata.listen, 'localhost', function() { | ||
| return conn.write("DELALIAS " + regId + " " + alias + "\x03"); | ||
| }); | ||
| }); | ||
| } | ||
| }, | ||
| config: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| return config.readConfig(opts.options.config, function(err, cpath, data) { | ||
| var onExists, regPath, regUrl; | ||
| if (err != null) { | ||
| return warn(err); | ||
| } | ||
| regPath = data.server.registryfile; | ||
| regUrl = data.server.registryUrl; | ||
| if ((regPath == null) && (regUrl == null)) { | ||
| return warn("No registry in config."); | ||
| } | ||
| onExists = function(exists) { | ||
| if (exists) { | ||
| return util.verifyConfig(cpath, data, function(err) { | ||
| if (err) { | ||
| return warn(err); | ||
| } | ||
| registry = data.server.registry; | ||
| delete data.server.registry; | ||
| return registry.size(function(err, sz) { | ||
| if (err != null) { | ||
| return warn(err); | ||
| } | ||
| if (sz > 0) { | ||
| say("Registry (configured):"); | ||
| registry.each((function(err, arg) { | ||
| var alias, alss, fingerprint, i, id, len, port, results; | ||
| port = arg.port, id = arg.id, fingerprint = arg.fingerprint, alss = arg.alss; | ||
| if (err != null) { | ||
| return warn("Registry err: ", err); | ||
| } else { | ||
| say(" id: " + id + ", fingerprint: " + fingerprint); | ||
| if (alss != null ? alss.length : void 0) { | ||
| results = []; | ||
| for (i = 0, len = alss.length; i < len; i++) { | ||
| alias = alss[i]; | ||
| results.push(say(" a.k.a. " + alias)); | ||
| } | ||
| return results; | ||
| } | ||
| } | ||
| }), function() { | ||
| return registry.quit(); | ||
| }); | ||
| } else { | ||
| registry.quit(); | ||
| say("Server registry is empty."); | ||
| } | ||
| say("Verified configuration file at " + cpath + "."); | ||
| return say("\"config\":", JSON.stringify(data.server, null, 2)); | ||
| }); | ||
| }); | ||
| } else { | ||
| say("This server has no registry."); | ||
| say("Verified configuration file at " + cpath + "."); | ||
| return say("\"config\":", JSON.stringify(data.server, null, 2)); | ||
| } | ||
| }; | ||
| if (regUrl == null) { | ||
| return fs.exists(regPath, onExists); | ||
| } else { | ||
| return onExists(true); | ||
| } | ||
| }); | ||
| } | ||
| }, | ||
| version: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| var obj; | ||
| obj = config.version(); | ||
| if ((obj.display != null) && obj.version !== obj.display) { | ||
| say("Ground2Cloud server version " + obj.version + " (d.b.a. " + obj.display + ")"); | ||
| } else { | ||
| say("Ground2Cloud server version " + obj.version); | ||
| } | ||
| return say("by Cloud Elements <cloud-elements.com>"); | ||
| } | ||
| }, | ||
| verifypatches: { | ||
| opts: [], | ||
| run: function(opts) { | ||
| return config.readConfig(opts.options.config, function(err, path, data) { | ||
| var onExists, regPath, regUrl; | ||
| if (err != null) { | ||
| return warn(err); | ||
| } | ||
| regPath = data.server.registryfile; | ||
| regUrl = data.server.registryUrl; | ||
| if ((regPath == null) && (regUrl == null)) { | ||
| return warn("No registry in config."); | ||
| } | ||
| onExists = function(exists) { | ||
| if (exists) { | ||
| return util.verifyConfig(path, data, function(err) { | ||
| registry = data.server.registry; | ||
| registry.quit(); | ||
| if (err) { | ||
| return warn(err); | ||
| } | ||
| patches = data.server.patches; | ||
| return patches.verifyAll(function(err) { | ||
| if (err != null) { | ||
| return warn("Bad patches", err); | ||
| } | ||
| return say("Good patches"); | ||
| }); | ||
| }); | ||
| } | ||
| }; | ||
| if (regUrl == null) { | ||
| return fs.exists(regPath, onExists); | ||
| } else { | ||
| return onExists(true); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
| }; | ||
| main(); | ||
| }).call(this); |
| #!/usr/bin/env node | ||
| (function() { | ||
| var SEVERE, _, cfgPath, config, errors, getopt, handle, handleConfig, level, lock, main, opt, output, restapi, server, startRest, util; | ||
| _ = require('lodash'); | ||
| getopt = require('node-getopt'); | ||
| config = require('../config'); | ||
| errors = require('../errors'); | ||
| lock = require('../lock'); | ||
| server = require('./server'); | ||
| util = require('./util'); | ||
| restapi = require('./restapi'); | ||
| opt = getopt.create([['v', 'verbose', 'print debug output'], ['c', 'config=ARG', 'set config file'], ['h', 'help', 'display this help']]).bindHelp().parseSystem(); | ||
| SEVERE = errors.SEVERE; | ||
| cfgPath = config.pickConfig(opt.options.config); | ||
| level = opt.options.verbose ? 'verbose' : 'normal'; | ||
| output = null; | ||
| main = function() { | ||
| return config.readConfig(opt.options.config, handleConfig); | ||
| }; | ||
| handleConfig = function(err, configPath, configData) { | ||
| if (err != null) { | ||
| return output.panic("Error reading config \"" + configPath + "\":", err); | ||
| } | ||
| return util.verifyConfig(configPath, configData, function(err) { | ||
| if (err != null) { | ||
| return output.prettyConfig(SEVERE, err); | ||
| } | ||
| return lock.lock('server', { | ||
| pid: process.pid, | ||
| listen: configData.server.listenPort | ||
| }, configPath, configData, function(err, v) { | ||
| var ref, sv; | ||
| sv = server.create(); | ||
| if ((ref = configData.server) != null) { | ||
| ref.configPath = configData.configPath; | ||
| } | ||
| handle(sv, configData.server); | ||
| return sv.start(configData.server); | ||
| }); | ||
| }); | ||
| }; | ||
| handle = function(sv, cdata) { | ||
| sv.on('error', function() {}); | ||
| sv.on('start', function() { | ||
| return output.log('Server started'); | ||
| }); | ||
| sv.on('close', function(hadErr) { | ||
| return output.log('Server closed'); | ||
| }); | ||
| sv.on('command', function(cmd, c) { | ||
| var alias, ref, ref1, ref2, regId; | ||
| output.log('Server command', cmd); | ||
| switch (false) { | ||
| case !_.startsWith(cmd, 'STATUS'): | ||
| ref = cmd.split(' '), cmd = ref[0], regId = ref[1]; | ||
| if (regId != null) { | ||
| return sv.statusOf(regId, function(err, stt) { | ||
| if (err) { | ||
| return c.end({ | ||
| success: false, | ||
| error: err.toString() | ||
| }); | ||
| } else { | ||
| return c.end(stt); | ||
| } | ||
| }); | ||
| } else { | ||
| return sv.status(function(err, stt) { | ||
| if (err) { | ||
| return c.end({ | ||
| success: false, | ||
| error: err.toString() | ||
| }); | ||
| } else { | ||
| return c.end(stt); | ||
| } | ||
| }); | ||
| } | ||
| break; | ||
| case cmd !== 'STOP': | ||
| return sv.end(); | ||
| case !_.startsWith(cmd, 'ADDALIAS'): | ||
| ref1 = cmd.split(' '), cmd = ref1[0], regId = ref1[1], alias = ref1[2]; | ||
| return cdata.registry.addAlias(regId, alias, function(err) { | ||
| if (err != null) { | ||
| return c.end({ | ||
| success: false, | ||
| error: err.toString() | ||
| }); | ||
| } else { | ||
| return c.end({ | ||
| success: true | ||
| }); | ||
| } | ||
| }); | ||
| case !_.startsWith(cmd, 'DELALIAS'): | ||
| ref2 = cmd.split(' '), cmd = ref2[0], regId = ref2[1], alias = ref2[2]; | ||
| return cdata.registry.removeAlias(regId, alias, function(err) { | ||
| if (err != null) { | ||
| return c.end({ | ||
| success: false, | ||
| error: err.toString() | ||
| }); | ||
| } else { | ||
| return c.end({ | ||
| success: true | ||
| }); | ||
| } | ||
| }); | ||
| default: | ||
| return output.warn("Unknown command " + cmd); | ||
| } | ||
| }); | ||
| sv.listener.on('error', function(err) { | ||
| return output.prettyListen(SEVERE, err, cdata); | ||
| }); | ||
| sv.listener.on('close', function() { | ||
| return output.log('Command listener closed.'); | ||
| }); | ||
| sv.listener.on('connection', function(c) { | ||
| output.log('Command stream opened.'); | ||
| c.on('error', function(err) { | ||
| return output.prettyListen(SEVERE, err, cdata); | ||
| }); | ||
| return c.on('close', function() { | ||
| return output.log('Command stream closed.'); | ||
| }); | ||
| }); | ||
| sv.listener.on('listening', function(addr) { | ||
| return output.log('Command listening on', addr); | ||
| }); | ||
| sv.backchannel.on('error', function(err) { | ||
| return output.prettyBacktalk(SEVERE, err, cdata); | ||
| }); | ||
| sv.backchannel.on('close', function() { | ||
| return output.log('Backchannel closed.'); | ||
| }); | ||
| sv.backchannel.on('listening', function(addr) { | ||
| return output.log('Backchannel listening on', addr); | ||
| }); | ||
| sv.backchannel.on('connection', function(c) { | ||
| output.log('Backchannel stream opened.'); | ||
| c.on('error', function(err) { | ||
| return output.prettyBacktalk(SEVERE, err, cdata); | ||
| }); | ||
| c.on('close', function(hadErr, rid) { | ||
| if (rid != null) { | ||
| return output.log("Backchannel " + rid + " stream closed."); | ||
| } else { | ||
| return output.log("Backchannel stream closed."); | ||
| } | ||
| }); | ||
| c.on('handshake', function(sp, rid) { | ||
| return output.log("Backchannel " + rid + " handshake complete."); | ||
| }); | ||
| return c.on('replacement', function(rid) { | ||
| return output.log("Replacing regID " + rid + "."); | ||
| }); | ||
| }); | ||
| sv.backchannel.on('registered', function() { | ||
| return output.log('Registered.'); | ||
| }); | ||
| sv.backchannel.on('unregistered', function() { | ||
| return output.log('Unregistered.'); | ||
| }); | ||
| sv.proxy.on('nokey', function() { | ||
| return output.log('No https proxy: no key file.'); | ||
| }); | ||
| sv.proxy.on('error', function(err) { | ||
| return output.prettyProxy(SEVERE, null, err, cdata); | ||
| }); | ||
| sv.proxy.on('close', function(err) { | ||
| return output.log('Proxy closed.'); | ||
| }); | ||
| sv.proxy.on('listening', function() { | ||
| return output.log('Proxy listening.'); | ||
| }); | ||
| return sv.proxy.on('connection', function(c, regId) { | ||
| output.log("Proxy stream for " + regId + " opened."); | ||
| c.on('error', function(err) { | ||
| return output.prettyProxy(SEVERE, regId, err, cdata); | ||
| }); | ||
| return c.on('close', function(hadErr) { | ||
| return output.log("Proxy stream for " + regId + " closed."); | ||
| }); | ||
| }); | ||
| }; | ||
| startRest = function() { | ||
| return config.readConfig(opt.options.config, function(err, cfgPath, cfgData) { | ||
| var apiPort, ref, ref1; | ||
| if (err != null) { | ||
| return output.warn("Can't start REST API: " + err); | ||
| } | ||
| apiPort = (ref = (ref1 = cfgData.server) != null ? ref1.apiPort : void 0) != null ? ref : 8101; | ||
| output.log("Starting REST API server on " + apiPort); | ||
| return restapi.start(apiPort, opt, output); | ||
| }); | ||
| }; | ||
| errors.init(cfgPath, 'server', level, function(err, out) { | ||
| if (err != null) { | ||
| return console.error('Unable to initialize logger', err); | ||
| } | ||
| output = out; | ||
| startRest(); | ||
| return main(); | ||
| }); | ||
| }).call(this); |
| #!/usr/bin/env node | ||
| (function() { | ||
| var _, async, fs, rclient, redis, regUrl, warn, | ||
| slice = [].slice; | ||
| _ = require('lodash'); | ||
| redis = require('redis'); | ||
| async = require('async'); | ||
| fs = require('fs'); | ||
| regUrl = 'redis://localhost:6379'; | ||
| rclient = redis.createClient(regUrl); | ||
| warn = function() { | ||
| var args; | ||
| args = 1 <= arguments.length ? slice.call(arguments, 0) : []; | ||
| console.log.apply(console, args); | ||
| return rclient.quit(); | ||
| }; | ||
| rclient.del('g2c/openPorts', function(err) { | ||
| var removeBads, takenPorts; | ||
| if (err != null) { | ||
| return console.log(err); | ||
| } | ||
| takenPorts = {}; | ||
| rclient.smembers('g2c/allids', function(err, ids) { | ||
| return async.each(ids, function(id, cb) { | ||
| return rclient.get("g2c/ids/" + id + "/port", function(err, port) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if (takenPorts[port] != null) { | ||
| console.log("Conflict on " + port + ": " + takenPorts[port] + " and " + id); | ||
| return cb(null); | ||
| } | ||
| takenPorts[port] = id; | ||
| return rclient.set("g2c/ports/" + port, id, cb); | ||
| }); | ||
| }, function(err) { | ||
| var i, j, results; | ||
| if (err != null) { | ||
| return console.log(err); | ||
| } | ||
| results = []; | ||
| for (i = j = 11080; j <= 64000; i = ++j) { | ||
| results.push((function(i) { | ||
| if (takenPorts[i] == null) { | ||
| console.log("available: " + i); | ||
| rclient.zadd('g2c/openPorts', 0, i, function(err) { | ||
| if (err != null) { | ||
| return console.log(err); | ||
| } | ||
| }); | ||
| } | ||
| if (i === 64000) { | ||
| return removeBads(function() { | ||
| return rclient.quit(); | ||
| }); | ||
| } | ||
| })(i)); | ||
| } | ||
| return results; | ||
| }); | ||
| }); | ||
| return removeBads = function(cb) { | ||
| console.log("removing bads"); | ||
| return rclient.keys('g2c/ports/*', function(err, pkeys) { | ||
| console.log("got " + pkeys.length + " pkeys"); | ||
| return async.each(pkeys, function(pkey, cb) { | ||
| var port, slash; | ||
| slash = pkey.lastIndexOf('/'); | ||
| port = pkey.substr(slash + 1); | ||
| if (takenPorts[port] == null) { | ||
| console.log("deleting " + pkey); | ||
| return rclient.del(pkey, cb); | ||
| } else { | ||
| return cb(null); | ||
| } | ||
| }, function(err) { | ||
| if (err != null) { | ||
| return console.log(err); | ||
| } | ||
| return cb(); | ||
| }); | ||
| }); | ||
| }; | ||
| }); | ||
| }).call(this); |
| (function() { | ||
| var _, async, createTlsOpts, crypto, events, extractHost, findEntry, fs, http, https, listen, open, path, proxy; | ||
| async = require('async'); | ||
| path = require('path'); | ||
| fs = require('fs'); | ||
| _ = require('lodash'); | ||
| events = require('events'); | ||
| http = require('http'); | ||
| https = require('https'); | ||
| proxy = require('http-proxy'); | ||
| crypto = require('crypto'); | ||
| exports.create = function() { | ||
| var protocol; | ||
| protocol = new events.EventEmitter(); | ||
| protocol.listen = function(lt, bt) { | ||
| return listen(protocol, lt, bt); | ||
| }; | ||
| return protocol; | ||
| }; | ||
| listen = function(protocol, lt, bt) { | ||
| protocol.config = bt.config; | ||
| return open(lt, bt, protocol); | ||
| }; | ||
| open = function(lt, bt, protocol) { | ||
| var createListen; | ||
| _.defaults(protocol.config, { | ||
| proxyPort: 8090, | ||
| secureProxyPort: 8453 | ||
| }); | ||
| createListen = function(isSec, isLocalSec, tlsOpts) { | ||
| var handler, px, registry, target, web; | ||
| target = !isLocalSec ? function(port) { | ||
| return "http://127.0.0.1:" + port; | ||
| } : function(port) { | ||
| return "https://127.0.0.1:" + port; | ||
| }; | ||
| registry = protocol.config.registry; | ||
| handler = function(req, res) { | ||
| var cpt, host, port, ref; | ||
| cpt = new events.EventEmitter(); | ||
| req.on('error', function(err) { | ||
| return cpt.emit('error', err); | ||
| }); | ||
| res.on('error', function(err) { | ||
| return cpt.emit('error', err); | ||
| }); | ||
| req.on('end', function() { | ||
| return cpt.emit('close', false); | ||
| }); | ||
| req.on('close', function() { | ||
| return cpt.emit('close', true); | ||
| }); | ||
| ref = extractHost(req.headers.host, 80), host = ref[0], port = ref[1]; | ||
| if (host == null) { | ||
| protocol.emit('error', "No host header in proxy request"); | ||
| res.write(JSON.stringify({ | ||
| success: false, | ||
| msg: "Proxy error: No host header" | ||
| })); | ||
| return res.end(); | ||
| } | ||
| return findEntry(protocol.config, host, function(err, regId, entry) { | ||
| var brand, ref1, ref2, ref3; | ||
| brand = (ref1 = entry != null ? (ref2 = entry.vitae) != null ? ref2.brand : void 0 : void 0) != null ? ref1 : 'ce'; | ||
| req.extras = { | ||
| regId: regId, | ||
| port: (ref3 = entry != null ? entry.port : void 0) != null ? ref3 : 0, | ||
| brand: brand | ||
| }; | ||
| if (err != null) { | ||
| protocol.emit('error', err); | ||
| res.statusCode = 404; | ||
| console.log('recording unavailable for', regId); | ||
| registry.used(regId, brand, new Date().getTime(), { | ||
| success: false, | ||
| reason: "service unavailable", | ||
| statusCode: res.statusCode | ||
| }); | ||
| res.write(JSON.stringify({ | ||
| g2c: 'unavailable' | ||
| })); | ||
| return res.end(); | ||
| } | ||
| protocol.emit('connection', cpt, regId); | ||
| return px.web(req, res, { | ||
| target: target(entry.port), | ||
| secure: false | ||
| }); | ||
| }); | ||
| }; | ||
| px = proxy.createProxyServer(); | ||
| web = !isSec ? http.createServer(handler) : https.createServer(tlsOpts, handler); | ||
| px.on('error', function(err, req, res) { | ||
| var brand, ref, ref1, regId; | ||
| protocol.emit('error', err); | ||
| ref1 = (ref = req.extras) != null ? ref : {}, regId = ref1.regId, brand = ref1.brand; | ||
| res.statusCode = 500; | ||
| registry.used(regId, brand, new Date().getTime(), { | ||
| success: false, | ||
| reason: "service unavailable", | ||
| statusCode: res.statusCode | ||
| }); | ||
| res.write(JSON.stringify({ | ||
| g2c: 'unavailable' | ||
| })); | ||
| return res.end(); | ||
| }); | ||
| px.on('proxyRes', function(proxyRes, req, res) { | ||
| var brand, ref, regId; | ||
| if ((regId = (ref = req.extras) != null ? ref.regId : void 0) != null) { | ||
| brand = req.extras.brand; | ||
| if (proxyRes.statusCode === 200) { | ||
| return registry.used(regId, brand, new Date().getTime(), { | ||
| success: true, | ||
| req: { | ||
| headers: req.headers | ||
| }, | ||
| res: { | ||
| headers: proxyRes.headers | ||
| }, | ||
| statusCode: proxyRes.statusCode | ||
| }); | ||
| } else { | ||
| return registry.used(regId, brand, new Date().getTime(), { | ||
| success: false, | ||
| reason: "service error", | ||
| req: { | ||
| headers: req.headers | ||
| }, | ||
| res: { | ||
| headers: proxyRes.headers | ||
| }, | ||
| statusCode: proxyRes.statusCode | ||
| }); | ||
| } | ||
| } else { | ||
| return protocol.emit('warn', "Couldn't find request regId to record"); | ||
| } | ||
| }); | ||
| web.on('error', function(err) { | ||
| return protocol.emit('error', err); | ||
| }); | ||
| web.on('listening', function() { | ||
| protocol.on('ending', function() { | ||
| return web.close(); | ||
| }); | ||
| return protocol.emit('listening'); | ||
| }); | ||
| return web.listen(!isSec ? protocol.config.proxyPort : protocol.config.secureProxyPort); | ||
| }; | ||
| return createTlsOpts(protocol, protocol.config, function(err, tlsOpts) { | ||
| if (err != null) { | ||
| return protocol.emit('error', err); | ||
| } | ||
| protocol.end = function() { | ||
| return protocol.emit('ending'); | ||
| }; | ||
| createListen(false, false); | ||
| if (tlsOpts != null) { | ||
| return createListen(true, false, tlsOpts); | ||
| } else { | ||
| return protocol.emit('nokey'); | ||
| } | ||
| }); | ||
| }; | ||
| extractHost = function(hostport, defport) { | ||
| var colon; | ||
| if (hostport == null) { | ||
| return [null, defport]; | ||
| } | ||
| colon = hostport.indexOf(':'); | ||
| if (colon === -1) { | ||
| return [hostport, defport]; | ||
| } else { | ||
| return [hostport.substr(0, colon), parseInt(hostport.substr(colon + 1))]; | ||
| } | ||
| }; | ||
| findEntry = function(config, host, cb) { | ||
| var registry, testre; | ||
| testre = /^\d+$/; | ||
| registry = config.registry; | ||
| return registry.findAlias(host, function(err, regId) { | ||
| var dash, dot; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if (!regId) { | ||
| dash = host.indexOf('-'); | ||
| if (dash > -1) { | ||
| regId = host.substring(0, dash); | ||
| if (!testre.test(regId)) { | ||
| regId = null; | ||
| } | ||
| } | ||
| if (!regId) { | ||
| dot = host.indexOf('.'); | ||
| if (dot > -1) { | ||
| regId = host.substring(0, dot); | ||
| if (!testre.test(regId)) { | ||
| return cb(new Error("Couldn't determine registeredId from " + host)); | ||
| } | ||
| } else { | ||
| return cb(new Error("Couldn't determine registeredId from " + host)); | ||
| } | ||
| } | ||
| } | ||
| return registry.get(regId, function(err, entry) { | ||
| if (err != null) { | ||
| return cb(err, regId); | ||
| } | ||
| if (registry.isConnected(regId)) { | ||
| return cb(null, regId, entry); | ||
| } else { | ||
| return cb(new Error(regId + " is not running."), regId); | ||
| } | ||
| }); | ||
| }); | ||
| }; | ||
| createTlsOpts = function(protocol, config, cb) { | ||
| var certDir, ciphers, honorCipherOrder, keyPath, secureOptions; | ||
| ciphers = 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK'; | ||
| secureOptions = crypto.constants.SSL_OP_NO_TLSv1; | ||
| honorCipherOrder = true; | ||
| certDir = path.join(path.dirname(config.configPath), 'certificates'); | ||
| keyPath = path.join(certDir, 'server.key'); | ||
| return fs.exists(keyPath, function(exists) { | ||
| if (!exists) { | ||
| return cb(null, null); | ||
| } | ||
| return fs.readFile(keyPath, function(err, key) { | ||
| if (err != null) { | ||
| return cb("No key file certificates/server.key: " + err); | ||
| } | ||
| return fs.readFile(path.join(certDir, 'server.crt'), function(err, cert) { | ||
| if (err != null) { | ||
| return cb("No certificate certificates/server.crt: " + err); | ||
| } | ||
| return fs.readFile(path.join(certDir, 'ca.bundle'), function(err, ca) { | ||
| return cb(err, { | ||
| secureOptions: secureOptions, | ||
| key: key, | ||
| cert: cert, | ||
| ciphers: ciphers, | ||
| honorCipherOrder: honorCipherOrder, | ||
| ca: ca | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| }).call(this); |
| #!/usr/bin/env node | ||
| (function() { | ||
| var _, async, fs, rclient, redis, regFn, regUrl, warn, | ||
| slice = [].slice; | ||
| _ = require('lodash'); | ||
| redis = require('redis'); | ||
| async = require('async'); | ||
| fs = require('fs'); | ||
| if (process.argv.length < 3) { | ||
| return console.warn("Usage: reg-convert <registryName>"); | ||
| } | ||
| regFn = process.argv[2]; | ||
| regUrl = 'redis://localhost:6379'; | ||
| rclient = redis.createClient(regUrl); | ||
| warn = function() { | ||
| var args; | ||
| args = 1 <= arguments.length ? slice.call(arguments, 0) : []; | ||
| console.warn.apply(console, args); | ||
| return rclient.quit(); | ||
| }; | ||
| fs.readFile(regFn, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| var ex, max, maxPort, ref, ref1, registry; | ||
| if (err != null) { | ||
| return warn("Unable to read " + regFn + ": " + err); | ||
| } | ||
| try { | ||
| registry = JSON.parse(data); | ||
| } catch (error) { | ||
| ex = error; | ||
| if (err != null) { | ||
| return warn("Unable to parse " + regFn + ": " + err); | ||
| } | ||
| } | ||
| max = Math.max(4000, (ref = registry.maxRegisteredId) != null ? ref : 0); | ||
| maxPort = Math.max(14000, (ref1 = registry.maxRegisteredPort) != null ? ref1 : 0); | ||
| return async.eachSeries(Object.keys(registry.registeredIds), function(regId, cb) { | ||
| var key, port, ref2, vitae; | ||
| ref2 = registry.registeredIds[regId], port = ref2.port, key = ref2.key, vitae = ref2.vitae; | ||
| if (/^\d+$/.test(regId)) { | ||
| max = Math.max(max, parseInt(regId)); | ||
| } | ||
| return async.series([ | ||
| function(cb) { | ||
| if (vitae != null) { | ||
| return rclient.set("g2c/ids/" + regId + "/vitae", JSON.stringify(vitae), cb); | ||
| } else { | ||
| return cb(); | ||
| } | ||
| }, function(cb) { | ||
| return rclient.set("g2c/ids/" + regId + "/key", key, cb); | ||
| }, function(cb) { | ||
| return rclient.set("g2c/ids/" + regId + "/port", port, cb); | ||
| }, function(cb) { | ||
| return rclient.set("g2c/ports/" + port, regId, cb); | ||
| }, function(cb) { | ||
| return rclient.sadd("g2c/allids", regId, cb); | ||
| } | ||
| ], cb); | ||
| }, function(err) { | ||
| return async.series([ | ||
| function(cb) { | ||
| return rclient.set("g2c/max/port", "" + maxPort, cb); | ||
| }, function(cb) { | ||
| return rclient.set("g2c/max/id", "" + max, cb); | ||
| } | ||
| ], function(err) { | ||
| console.log("Done"); | ||
| return rclient.quit(); | ||
| }); | ||
| }); | ||
| }); | ||
| }).call(this); |
| (function() { | ||
| var _, async, createManager, elastic, initializeEs; | ||
| _ = require('lodash'); | ||
| elastic = require('elasticsearch'); | ||
| async = require('async'); | ||
| exports.init = function(configPath, configData, cb) { | ||
| var esUrl, ref; | ||
| esUrl = configData != null ? (ref = configData.server) != null ? ref.esUrl : void 0 : void 0; | ||
| return initializeEs(esUrl, cb); | ||
| }; | ||
| initializeEs = function(esUrl, cb) { | ||
| var esClient; | ||
| if (esUrl == null) { | ||
| esUrl = 'elastic://localhost:9200'; | ||
| } | ||
| if (!esUrl.startsWith('elastic://')) { | ||
| return cb("Unknown stats type " + esUrl); | ||
| } | ||
| esUrl = esUrl.substr("elastic://".length); | ||
| esClient = new elastic.Client({ | ||
| host: esUrl, | ||
| log: 'warn' | ||
| }); | ||
| return cb(null); | ||
| }; | ||
| exports.manage = function(configPath, configData, cb) { | ||
| var esUrl, ref; | ||
| esUrl = configData != null ? (ref = configData.server) != null ? ref.esUrl : void 0 : void 0; | ||
| return createManager(esUrl, cb); | ||
| }; | ||
| createManager = function(esUrl, cb) { | ||
| var counts, esClient, usage, used; | ||
| if (esUrl == null) { | ||
| esUrl = 'elastic://localhost:9200'; | ||
| } | ||
| if (!esUrl.startsWith('elastic://')) { | ||
| return cb("Unknown stats type " + esUrl); | ||
| } | ||
| esUrl = esUrl.substr("elastic://".length); | ||
| esClient = new elastic.Client({ | ||
| host: esUrl, | ||
| log: 'warn' | ||
| }); | ||
| used = function(id, brand, score, data, cb) { | ||
| data.timestamp = score; | ||
| data.date = new Date(score).toString(); | ||
| data.regId = id; | ||
| data.brand = brand; | ||
| return esClient.index({ | ||
| index: 'g2c', | ||
| type: 'usage', | ||
| body: data | ||
| }, cb); | ||
| }; | ||
| usage = function(id, start, end, cb) { | ||
| return esClient.search({ | ||
| index: 'g2c', | ||
| type: 'usage', | ||
| body: { | ||
| query: { | ||
| range: { | ||
| timestamp: { | ||
| gte: start, | ||
| lte: end | ||
| } | ||
| }, | ||
| term: { | ||
| regId: id | ||
| } | ||
| } | ||
| } | ||
| }, cb); | ||
| }; | ||
| counts = function(type, code, brand, start, end, cb) { | ||
| var search, total; | ||
| total = 0; | ||
| search = ['successes']; | ||
| if (type === 'unavailable') { | ||
| search = ['unavailables']; | ||
| } else if (type === 'error') { | ||
| search = ['errors']; | ||
| } else if (type === 'success') { | ||
| search = ['successes']; | ||
| } else if (type === 'errors') { | ||
| search = ['unavailables', 'errors']; | ||
| } else if (type === 'requests') { | ||
| search = ['errors', 'unavailables', 'successes']; | ||
| } | ||
| return async.forEachSeries(search, function(type, cb) { | ||
| if (code != null) { | ||
| return esClient.zrangebyscore("g2c/" + type + "/" + brand, start, end, function(err, dats) { | ||
| var filtered; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| filtered = _.filter(dats, function(d) { | ||
| var parsed; | ||
| try { | ||
| parsed = JSON.parse(d); | ||
| return ('' + parsed.code) === code; | ||
| } catch (error) { | ||
| return false; | ||
| } | ||
| }); | ||
| total += filtered.length; | ||
| return cb(); | ||
| }); | ||
| } else { | ||
| return esClient.zcount("g2c/" + type + "/" + brand, start, end, function(err, dats) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| total += dats; | ||
| return cb(); | ||
| }); | ||
| } | ||
| }, function(err) { | ||
| return cb(err, total); | ||
| }); | ||
| }; | ||
| return cb(null, { | ||
| used: used, | ||
| usage: usage, | ||
| counts: counts, | ||
| quit: function() { | ||
| return esClient.close(); | ||
| } | ||
| }); | ||
| }; | ||
| }).call(this); |
| (function() { | ||
| var _, akPath, async, createAuthkeys, createManager, forge, fs, home, mkdirp, path, redis, shPath, | ||
| slice = [].slice; | ||
| _ = require('lodash'); | ||
| redis = require('redis'); | ||
| async = require('async'); | ||
| fs = require('fs'); | ||
| path = require('path'); | ||
| mkdirp = require('mkdirp'); | ||
| forge = require('node-forge'); | ||
| home = process.env.HOME || process.env.USERPROFILE; | ||
| shPath = path.join(home, '.ssh'); | ||
| akPath = path.join(shPath, 'authorized_keys'); | ||
| exports.init = function(configPath, configData, cb) { | ||
| var cb2, ref, regUrl; | ||
| regUrl = configData != null ? (ref = configData.server) != null ? ref.registryfile : void 0 : void 0; | ||
| if (regUrl == null) { | ||
| return cb(new Error("No registryUrl in configuration")); | ||
| } | ||
| cb2 = createAuthkeys(regUrl, cb); | ||
| return cb2(null, true); | ||
| }; | ||
| createAuthkeys = function(regUrl, cb) { | ||
| return function(err, made) { | ||
| if (err != null) { | ||
| return cb(err, made, regUrl); | ||
| } | ||
| return fs.exists(akPath, function(exists) { | ||
| if (exists) { | ||
| return cb(null, made, regUrl); | ||
| } | ||
| return mkdirp(shPath, 0x1c0, function(err) { | ||
| if (err != null) { | ||
| return cb(err, made, regUrl); | ||
| } | ||
| return fs.writeFile(akPath, '', { | ||
| encoding: 'ascii', | ||
| mode: 0x180 | ||
| }, function(err) { | ||
| return cb(err, made, regUrl); | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| }; | ||
| exports.manage = function(configPath, configData, cb) { | ||
| var ref, regUrl; | ||
| regUrl = configData != null ? (ref = configData.server) != null ? ref.registryUrl : void 0 : void 0; | ||
| if (regUrl == null) { | ||
| return cb(new Error("No registryUrl in configuration")); | ||
| } | ||
| return cb(null, createManager(regUrl)); | ||
| }; | ||
| createManager = function(redisUrl) { | ||
| var addAlias, addAuthorized, connected, connecting, connects, counts, disconnected, each, find, findAlias, findFromPref, findPortFromPref, get, isAvailable, isConnected, pickPort, rclient, register, registers, removeAlias, removeAuthorized, sameKey, setVitae, setnxGoodId, setnxGoodPort, size, status, statusOf, unregister, usage, used; | ||
| if (redisUrl == null) { | ||
| redisUrl = 'redis://localhost:6379'; | ||
| } | ||
| connects = {}; | ||
| rclient = redis.createClient(redisUrl); | ||
| status = function(cb) { | ||
| return rclient.smembers('g2c/allids', function(err, mems) { | ||
| var regId; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return cb(null, { | ||
| status: 'running', | ||
| connections: (function() { | ||
| var i, len, ref, results; | ||
| results = []; | ||
| for (i = 0, len = mems.length; i < len; i++) { | ||
| regId = mems[i]; | ||
| results.push({ | ||
| registeredId: regId, | ||
| connected: (ref = connects[regId]) != null ? ref : false | ||
| }); | ||
| } | ||
| return results; | ||
| })() | ||
| }); | ||
| }); | ||
| }; | ||
| statusOf = function(regId, cb) { | ||
| var keys; | ||
| keys = ['vitae', 'key', 'port'].map(function(p) { | ||
| return "g2c/ids/" + regId + "/" + p; | ||
| }); | ||
| return rclient.mget(keys, function(err, vals) { | ||
| var fpt, pubkey, vitae; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| pubkey = forge.pki.publicKeyFromPem(vals[1]); | ||
| fpt = forge.ssh.getPublicKeyFingerprint(pubkey, { | ||
| encoding: 'hex', | ||
| delimiter: ':' | ||
| }); | ||
| vitae = (function() { | ||
| try { | ||
| return JSON.parse(vals[0]); | ||
| } catch (error) { | ||
| return {}; | ||
| } | ||
| })(); | ||
| return rclient.smembers("g2c/ids/" + regId + "/aliases", function(err, als) { | ||
| var ref; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return cb(null, { | ||
| status: 'running', | ||
| connections: [ | ||
| { | ||
| registeredId: regId, | ||
| port: parseInt(vals[2]), | ||
| fingerprint: fpt, | ||
| connected: (ref = connects[regId]) != null ? ref : false, | ||
| vitae: vitae, | ||
| aliases: als | ||
| } | ||
| ] | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| addAlias = function(regId, alias, cb) { | ||
| return rclient.exists("g2c/aliases/" + alias, function(err, ex) { | ||
| if (ex) { | ||
| return cb("Alias " + alias + " already exists."); | ||
| } | ||
| return rclient.sismember('g2c/allids', regId, function(err, ex) { | ||
| if (!ex) { | ||
| return cb("No such registeredId " + regId + "."); | ||
| } | ||
| return rclient.sadd("g2c/ids/{regId}/aliases", alias, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return rclient.set("g2c/aliases/" + alias, regId, cb); | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| removeAlias = function(regId, alias, cb) { | ||
| return rclient.sismember("g2c/ids/" + regId + "/aliases", alias, function(err, ism) { | ||
| if (!ism) { | ||
| return cb(alias + " is not an alias of " + regId); | ||
| } | ||
| return rclient.srem("g2c/ids/" + regId + "/aliases", alias, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return rclient.del("g2c/aliases/" + alias, cb); | ||
| }); | ||
| }); | ||
| }; | ||
| sameKey = function(regIds, cb) { | ||
| var commonfpt, keys; | ||
| commonfpt = null; | ||
| keys = regIds.map(function(r) { | ||
| return "g2c/ids/" + r + "/key"; | ||
| }); | ||
| return rclient.mget(keys, function(err, vals) { | ||
| var e, fpt, i, len, pubkey, val; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| for (i = 0, len = vals.length; i < len; i++) { | ||
| val = vals[i]; | ||
| try { | ||
| pubkey = forge.pki.publicKeyFromPem(val); | ||
| fpt = forge.ssh.getPublicKeyFingerprint(pubkey, { | ||
| encoding: 'hex', | ||
| delimiter: ':' | ||
| }); | ||
| } catch (error) { | ||
| e = error; | ||
| return cb(e); | ||
| } | ||
| if (commonfpt == null) { | ||
| commonfpt = fpt; | ||
| } else if (commonfpt !== fpt) { | ||
| return cb(null, false); | ||
| } | ||
| } | ||
| return cb(null, true); | ||
| }); | ||
| }; | ||
| setVitae = function(regId, vitae, cb) { | ||
| console.log("setting vitae", vitae); | ||
| return rclient.sismember('g2c/allids', regId, function(err, ex) { | ||
| if (!ex) { | ||
| return typeof cb === "function" ? cb("No such registeredId " + regId + ".") : void 0; | ||
| } | ||
| if (vitae != null) { | ||
| return rclient.set("g2c/ids/" + regId + "/vitae", JSON.stringify(vitae), function(err) { | ||
| var brand, jdata, ref; | ||
| if (err != null) { | ||
| return typeof cb === "function" ? cb(err) : void 0; | ||
| } | ||
| brand = (ref = vitae.brand) != null ? ref : 'ce'; | ||
| jdata = JSON.stringify({ | ||
| brand: brand, | ||
| regId: regId | ||
| }); | ||
| return rclient.zrank("g2c/registers/" + brand, jdata, function(err, rnk) { | ||
| var score; | ||
| if (err != null) { | ||
| return typeof cb === "function" ? cb(err) : void 0; | ||
| } | ||
| if (rnk == null) { | ||
| score = new Date().getTime(); | ||
| return rclient.zadd("g2c/registers/" + brand, score, jdata, cb); | ||
| } | ||
| }); | ||
| }); | ||
| } else { | ||
| return rclient.del("g2c/ids/" + regId + "/vitae", cb); | ||
| } | ||
| }); | ||
| }; | ||
| findAlias = function(alias, cb) { | ||
| return rclient.get("g2c/aliases/" + alias, cb); | ||
| }; | ||
| isAvailable = function(regIds) { | ||
| var i, len, ref, regId, takens; | ||
| takens = []; | ||
| for (i = 0, len = regIds.length; i < len; i++) { | ||
| regId = regIds[i]; | ||
| if (!(((ref = connects[regId]) != null ? ref : false) === false)) { | ||
| takens.push(regId); | ||
| } | ||
| } | ||
| return takens; | ||
| }; | ||
| isConnected = function(regId) { | ||
| var ref; | ||
| return ((ref = connects[regId]) != null ? ref : false) === true; | ||
| }; | ||
| connected = function(regId) { | ||
| return connects[regId] = true; | ||
| }; | ||
| connecting = function(regIds) { | ||
| var i, len, regId, results; | ||
| results = []; | ||
| for (i = 0, len = regIds.length; i < len; i++) { | ||
| regId = regIds[i]; | ||
| results.push(connects[regId] = "connecting"); | ||
| } | ||
| return results; | ||
| }; | ||
| disconnected = function(regId) { | ||
| return connects[regId] = false; | ||
| }; | ||
| size = function(cb) { | ||
| return rclient.scard('g2c/allids', cb); | ||
| }; | ||
| get = function(regId, cb) { | ||
| var keys; | ||
| keys = ['vitae', 'key', 'port'].map(function(p) { | ||
| return "g2c/ids/" + regId + "/" + p; | ||
| }); | ||
| return rclient.mget(keys, function(err, vals) { | ||
| var entry; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if ((vals[2] == null) || (vals[1] == null)) { | ||
| return cb("No such registered ID \"" + regId + "\""); | ||
| } | ||
| entry = { | ||
| port: parseInt(vals[2]), | ||
| key: vals[1] | ||
| }; | ||
| try { | ||
| entry.vitae = JSON.parse(vals[0]); | ||
| } catch (error) {} | ||
| return rclient.smembers("g2c/ids/" + regId + "/aliases", function(err, als) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if ((als != null) && als.length) { | ||
| entry.aliases = als; | ||
| } | ||
| return cb(null, entry); | ||
| }); | ||
| }); | ||
| }; | ||
| find = function(regIds, cb) { | ||
| var pkeys; | ||
| if ((regIds == null) || regIds.length === 0) { | ||
| return cb(null, null, []); | ||
| } | ||
| pkeys = regIds.map(function(r) { | ||
| return "g2c/ids/" + r + "/port"; | ||
| }); | ||
| return rclient.mget(pkeys, function(err, pvals) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if (_.some(pvals, function(v) { | ||
| return v == null; | ||
| })) { | ||
| return cb("Ports missing for registeredIds " + regIds); | ||
| } | ||
| return rclient.get("g2c/ids/" + regIds[0] + "/key", function(err, kval) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if (kval == null) { | ||
| return cb("Key missing for " + regIds[0]); | ||
| } | ||
| return cb(null, kval, pvals); | ||
| }); | ||
| }); | ||
| }; | ||
| each = function(icb, cb) { | ||
| if (cb == null) { | ||
| cb = function() {}; | ||
| } | ||
| return rclient.smembers('g2c/allids', function(err, ids) { | ||
| var i, len, regId; | ||
| if (err != null) { | ||
| return icb(err); | ||
| } | ||
| for (i = 0, len = ids.length; i < len; i++) { | ||
| regId = ids[i]; | ||
| statusOf(regId, function(err, stat) { | ||
| var conn; | ||
| if (err != null) { | ||
| return icb(err); | ||
| } | ||
| conn = stat.connections[0]; | ||
| conn.id = conn.registeredId; | ||
| return icb(null, conn); | ||
| }); | ||
| } | ||
| return cb(null); | ||
| }); | ||
| }; | ||
| used = function(id, brand, score, data, cb) { | ||
| return typeof cb === "function" ? cb(null) : void 0; | ||
| }; | ||
| usage = function(id, start, end, cb) { | ||
| return rclient.zrangebyscore("g2c/ids/" + id + "/usage", start, end, function(err, dats) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return cb(null, _.map(dats, JSON.parse)); | ||
| }); | ||
| }; | ||
| counts = function(type, code, brand, start, end, cb) { | ||
| var search, total; | ||
| total = 0; | ||
| search = ['successes']; | ||
| if (type === 'unavailable') { | ||
| search = ['unavailables']; | ||
| } else if (type === 'error') { | ||
| search = ['errors']; | ||
| } else if (type === 'success') { | ||
| search = ['successes']; | ||
| } else if (type === 'errors') { | ||
| search = ['unavailables', 'errors']; | ||
| } else if (type === 'requests') { | ||
| search = ['errors', 'unavailables', 'successes']; | ||
| } | ||
| return async.forEachSeries(search, function(type, cb) { | ||
| if (code != null) { | ||
| return rclient.zrangebyscore("g2c/" + type + "/" + brand, start, end, function(err, dats) { | ||
| var filtered; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| filtered = _.filter(dats, function(d) { | ||
| var parsed; | ||
| try { | ||
| parsed = JSON.parse(d); | ||
| return ('' + parsed.code) === code; | ||
| } catch (error) { | ||
| return false; | ||
| } | ||
| }); | ||
| total += filtered.length; | ||
| return cb(); | ||
| }); | ||
| } else { | ||
| return rclient.zcount("g2c/" + type + "/" + brand, start, end, function(err, dats) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| total += dats; | ||
| return cb(); | ||
| }); | ||
| } | ||
| }, function(err) { | ||
| return cb(err, total); | ||
| }); | ||
| }; | ||
| registers = function(brand, start, end, cb) { | ||
| return rclient.zcount("g2c/registers/" + brand, start, end, cb); | ||
| }; | ||
| setnxGoodPort = function(port, prefId, cb) { | ||
| if (!_.isInteger(port) && /^\d+$/.test(port)) { | ||
| port = parseInt(port); | ||
| } | ||
| if (port == null) { | ||
| return cb(null, false); | ||
| } | ||
| if (!_.isInteger(port)) { | ||
| return cb(null, false); | ||
| } | ||
| if (port < 11081 || port > 64000) { | ||
| return cb(null, false); | ||
| } | ||
| return rclient.setnx("g2c/ports/" + port, prefId, function(err, prv) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if (prv) { | ||
| return rclient.zrem("g2c/openPorts", port, function(err) { | ||
| return cb(err, prv); | ||
| }); | ||
| } else { | ||
| return cb(err, prv); | ||
| } | ||
| }); | ||
| }; | ||
| pickPort = function(tries, prefId, cb) { | ||
| if (tries > 100) { | ||
| return cb("No available port range"); | ||
| } | ||
| return rclient.zrange('g2c/openPorts', 0, 100, function(err, rng) { | ||
| var attempt, attemptStr, e, index; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if (cb.length === 0) { | ||
| return cb("No available port"); | ||
| } | ||
| index = Math.floor(Math.random() * rng.length); | ||
| attemptStr = rng[index]; | ||
| try { | ||
| attempt = parseInt(attemptStr); | ||
| } catch (error) { | ||
| e = error; | ||
| return cb(e); | ||
| } | ||
| return rclient.setnx("g2c/ports/" + attempt, prefId, function(err, got) { | ||
| if (got) { | ||
| return rclient.zrem("g2c/openPorts", attempt, function(err) { | ||
| return cb(err, attempt); | ||
| }); | ||
| } else { | ||
| return pickPort(tries + 1, prefId, cb); | ||
| } | ||
| }); | ||
| }); | ||
| }; | ||
| findPortFromPref = function(pref, cb) { | ||
| var obtained; | ||
| obtained = {}; | ||
| return setnxGoodPort(pref.port, pref.id, function(err, prv) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if (prv) { | ||
| obtained.port = pref.port; | ||
| return rclient.zrem("g2c/openPorts", pref.port, function(err) { | ||
| return cb(err, obtained); | ||
| }); | ||
| } else { | ||
| return pickPort(0, pref.id, function(err, maxport) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| pref.port = obtained.port = maxport; | ||
| return rclient.set("g2c/ids/" + pref.id + "/port", maxport, function(err) { | ||
| return cb(err, obtained); | ||
| }); | ||
| }); | ||
| } | ||
| }); | ||
| }; | ||
| setnxGoodId = function(id, prefPort, cb) { | ||
| if (prefPort == null) { | ||
| prefPort = 0; | ||
| } | ||
| return rclient.setnx("g2c/max/id", 4000, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if (id == null) { | ||
| return cb(null, false); | ||
| } | ||
| if (typeof id !== 'string') { | ||
| return cb(null, false); | ||
| } | ||
| if (id.length < 4) { | ||
| return cb(null, false); | ||
| } | ||
| if (/^\d+$/.test(id)) { | ||
| return rclient.get("g2c/max/id", function(err, max) { | ||
| if (parseInt(id) > parseInt(max)) { | ||
| return cb(null, false); | ||
| } | ||
| return rclient.get("g2c/ids/" + id + "/port", function(err, oldPort) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if (oldPort != null) { | ||
| return rclient.del("g2c/ports/" + oldPort, function(err) { | ||
| var now; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| now = new Date().getTime(); | ||
| return rclient.zadd("g2c/openPorts", now, oldPort, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return rclient.set("g2c/ids/" + id + "/port", prefPort, cb); | ||
| }); | ||
| }); | ||
| } else { | ||
| return rclient.set("g2c/ids/" + id + "/port", prefPort, cb); | ||
| } | ||
| }); | ||
| }); | ||
| } else { | ||
| return rclient.get("g2c/ids/" + id + "/port", function(err, oldPort) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if (oldPort != null) { | ||
| return rclient.del("g2c/ports/" + oldPort, function(err) { | ||
| var now; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| now = new Date().getTime(); | ||
| return rclient.zadd("g2c/openPorts", now, oldPort, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return rclient.set("g2c/ids/" + id + "/port", prefPort, cb); | ||
| }); | ||
| }); | ||
| } else { | ||
| return rclient.set("g2c/ids/" + id + "/port", prefPort, cb); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| }; | ||
| findFromPref = function(pref, cb) { | ||
| var obtained; | ||
| obtained = {}; | ||
| return setnxGoodId(pref.id, pref.port, function(err, irv) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if (irv) { | ||
| obtained.id = pref.id; | ||
| return findPortFromPref(pref, function(err, pobt) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| obtained.port = pobt.port; | ||
| return rclient.set("g2c/ids/" + obtained.id + "/port", obtained.port, function(err) { | ||
| return cb(err, obtained); | ||
| }); | ||
| }); | ||
| } else { | ||
| return rclient.incr("g2c/max/id", function(err, maxid) { | ||
| var ref; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| pref.id = obtained.id = "" + maxid; | ||
| return rclient.set("g2c/ids/" + maxid + "/port", (ref = pref.port) != null ? ref : 0, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return findPortFromPref(pref, function(err, pobt) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| obtained.port = pobt.port; | ||
| return cb(null, obtained); | ||
| }); | ||
| }); | ||
| }); | ||
| } | ||
| }); | ||
| }; | ||
| register = function(pubkeypem, preferred, cb) { | ||
| var prefclone; | ||
| prefclone = _.cloneDeep(preferred); | ||
| return findFromPref(preferred != null ? preferred : {}, function(err, obtained) { | ||
| var pubkey, sshkey; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| console.log('from pref', prefclone, obtained); | ||
| try { | ||
| if (pubkeypem != null) { | ||
| pubkey = forge.pki.publicKeyFromPem(pubkeypem); | ||
| } | ||
| if (pubkey != null) { | ||
| sshkey = forge.ssh.publicKeyToOpenSSH(pubkey); | ||
| } | ||
| } catch (error) { | ||
| err = error; | ||
| return cb(err); | ||
| } | ||
| if (sshkey == null) { | ||
| return cb(new Error('Failed public key.')); | ||
| } | ||
| return rclient.set("g2c/ids/" + obtained.id + "/key", pubkeypem, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return rclient.sadd('g2c/allids', obtained.id, function(err) { | ||
| var entry; | ||
| entry = { | ||
| port: obtained.port, | ||
| key: pubkeypem | ||
| }; | ||
| return addAuthorized(sshkey, obtained.port, function(err) { | ||
| return cb(err, obtained.id, entry); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| unregister = function(regIds, cb) { | ||
| return async.eachSeries(regIds, function(regId, cb) { | ||
| var keys; | ||
| keys = ['vitae', 'key', 'port', 'aliases'].map(function(p) { | ||
| return "g2c/ids/" + regId + "/" + p; | ||
| }); | ||
| return rclient.sismember('g2c/allids', regId, function(err, ism) { | ||
| if (!ism) { | ||
| return cb("No entry found for " + regId); | ||
| } | ||
| return rclient.get("g2c/ids/" + regId + "/port", function(err, port) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return rclient.smembers("g2c/ids/" + regId + "/aliases", function(err, als) { | ||
| var akeys; | ||
| akeys = als.map(function(a) { | ||
| return "g2c/aliases/" + a; | ||
| }); | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return rclient.del.apply(rclient, slice.call(keys).concat([function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return rclient.del("g2c/ports/" + port, function(err) { | ||
| var now; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| now = new Date().getTime(); | ||
| return rclient.zadd("g2c/openPorts", now, port, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| if (akeys.length) { | ||
| return rclient.del.apply(rclient, slice.call(akeys).concat([function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return rclient.srem('g2c/allids', regId, cb); | ||
| }])); | ||
| } else { | ||
| return rclient.srem('g2c/allids', regId, cb); | ||
| } | ||
| }); | ||
| }); | ||
| }])); | ||
| }); | ||
| }); | ||
| }); | ||
| }, cb); | ||
| }; | ||
| addAuthorized = function(sshkey, port, cb) { | ||
| return fs.appendFile(akPath, "command=\"echo 'This login is restricted.'\",no-agent-forwarding,no-X11-forwarding,permitopen=\"localhost:" + port + "\" " + sshkey + "\n", { | ||
| encoding: 'utf8' | ||
| }, cb); | ||
| }; | ||
| removeAuthorized = function(port, cb) { | ||
| return fs.readFile(akPath, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| var lines, origLines; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| origLines = data.split('\n'); | ||
| lines = _.reject(origLines, function(l) { | ||
| return l.indexOf("localhost:" + port) > -1; | ||
| }); | ||
| return fs.writeFile(akPath, lines.join('\n'), { | ||
| encoding: 'utf8' | ||
| }, cb); | ||
| }); | ||
| }; | ||
| return { | ||
| setVitae: setVitae, | ||
| findAlias: findAlias, | ||
| addAlias: addAlias, | ||
| removeAlias: removeAlias, | ||
| status: status, | ||
| statusOf: statusOf, | ||
| connected: connected, | ||
| connecting: connecting, | ||
| isAvailable: isAvailable, | ||
| isConnected: isConnected, | ||
| disconnected: disconnected, | ||
| size: size, | ||
| each: each, | ||
| find: find, | ||
| get: get, | ||
| register: register, | ||
| unregister: unregister, | ||
| sameKey: sameKey, | ||
| used: used, | ||
| usage: usage, | ||
| counts: counts, | ||
| registers: registers, | ||
| quit: function() { | ||
| return rclient.quit(); | ||
| } | ||
| }; | ||
| }; | ||
| }).call(this); |
| (function() { | ||
| var _, akPath, async, createAuthkeys, createManager, forge, fs, home, mkdirp, path, shPath; | ||
| _ = require('lodash'); | ||
| async = require('async'); | ||
| fs = require('fs'); | ||
| path = require('path'); | ||
| mkdirp = require('mkdirp'); | ||
| forge = require('node-forge'); | ||
| home = process.env.HOME || process.env.USERPROFILE; | ||
| shPath = path.join(home, '.ssh'); | ||
| akPath = path.join(shPath, 'authorized_keys'); | ||
| exports.init = function(configPath, configData, cb) { | ||
| var cb2, ref, regPath; | ||
| regPath = configData != null ? (ref = configData.server) != null ? ref.registryfile : void 0 : void 0; | ||
| if (regPath == null) { | ||
| return cb(new Error("No registryfile in configuration")); | ||
| } | ||
| cb2 = createAuthkeys(regPath, cb); | ||
| return fs.exists(regPath, function(exists) { | ||
| var data; | ||
| if (exists) { | ||
| return cb2(null, false); | ||
| } else { | ||
| data = { | ||
| maxRegisteredId: 1000, | ||
| maxRegisteredPort: 11080, | ||
| comments: ['Keys used by petit serverd to verify identity and associate', 'Each key is also in the local authorized_keys file'], | ||
| version: 1, | ||
| registeredIds: {} | ||
| }; | ||
| return fs.writeFile(regPath, JSON.stringify(data, null, 2), { | ||
| encoding: 'utf8' | ||
| }, function(err) { | ||
| return cb2(err, true); | ||
| }); | ||
| } | ||
| }); | ||
| }; | ||
| createAuthkeys = function(regPath, cb) { | ||
| return function(err, made) { | ||
| if (err != null) { | ||
| return cb(err, made, regPath); | ||
| } | ||
| return fs.exists(akPath, function(exists) { | ||
| if (exists) { | ||
| return cb(null, made, regPath); | ||
| } | ||
| return mkdirp(shPath, 0x1c0, function(err) { | ||
| if (err != null) { | ||
| return cb(err, made, regPath); | ||
| } | ||
| return fs.writeFile(akPath, '', { | ||
| encoding: 'ascii', | ||
| mode: 0x180 | ||
| }, function(err) { | ||
| return cb(err, made, regPath); | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| }; | ||
| exports.manage = function(configPath, configData, cb) { | ||
| var ref, regPath; | ||
| regPath = configData != null ? (ref = configData.server) != null ? ref.registryfile : void 0 : void 0; | ||
| if (regPath == null) { | ||
| return cb(new Error("No registryfile in configuration")); | ||
| } | ||
| return fs.readFile(regPath, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| data = JSON.parse(data); | ||
| return cb(null, createManager(regPath, data)); | ||
| }); | ||
| }; | ||
| createManager = function(regPath, data) { | ||
| var addAlias, addAuthorized, alias, aliases, connected, connecting, connects, disconnected, each, entry, find, findAlias, get, i, isAvailable, isConnected, j, len, len1, ref, ref1, ref2, regId, register, removeAlias, removeAuthorized, sameKey, setVitae, size, status, statusOf, unregister, usage, used, validatePreference, write; | ||
| connects = {}; | ||
| aliases = {}; | ||
| ref = _.keys(data.registeredIds); | ||
| for (i = 0, len = ref.length; i < len; i++) { | ||
| regId = ref[i]; | ||
| entry = data.registeredIds[regId]; | ||
| ref2 = (ref1 = entry.aliases) != null ? ref1 : []; | ||
| for (j = 0, len1 = ref2.length; j < len1; j++) { | ||
| alias = ref2[j]; | ||
| aliases[alias] = regId; | ||
| } | ||
| } | ||
| write = function(cb) { | ||
| return fs.writeFile(regPath, JSON.stringify(data, null, 2), { | ||
| encoding: 'utf8' | ||
| }, cb); | ||
| }; | ||
| status = function(cb) { | ||
| var fpt, pubkey; | ||
| return cb(null, { | ||
| status: 'running', | ||
| connections: (function() { | ||
| var len2, m, ref3, ref4, results; | ||
| ref3 = _.keys(data.registeredIds); | ||
| results = []; | ||
| for (m = 0, len2 = ref3.length; m < len2; m++) { | ||
| regId = ref3[m]; | ||
| entry = data.registeredIds[regId]; | ||
| pubkey = forge.pki.publicKeyFromPem(entry.key); | ||
| fpt = forge.ssh.getPublicKeyFingerprint(pubkey, { | ||
| encoding: 'hex', | ||
| delimiter: ':' | ||
| }); | ||
| results.push({ | ||
| registeredId: regId, | ||
| port: entry.port, | ||
| fingerprint: fpt, | ||
| connected: (ref4 = connects[regId]) != null ? ref4 : false, | ||
| aliases: entry.aliases, | ||
| vitae: entry.vitae | ||
| }); | ||
| } | ||
| return results; | ||
| })() | ||
| }); | ||
| }; | ||
| statusOf = function(rid, cb) { | ||
| var fpt, pubkey; | ||
| return cb(null, { | ||
| status: 'running', | ||
| connections: (function() { | ||
| var len2, m, ref3, ref4, results; | ||
| ref3 = [rid]; | ||
| results = []; | ||
| for (m = 0, len2 = ref3.length; m < len2; m++) { | ||
| regId = ref3[m]; | ||
| entry = data.registeredIds[regId]; | ||
| pubkey = forge.pki.publicKeyFromPem(entry.key); | ||
| fpt = forge.ssh.getPublicKeyFingerprint(pubkey, { | ||
| encoding: 'hex', | ||
| delimiter: ':' | ||
| }); | ||
| results.push({ | ||
| registeredId: regId, | ||
| port: entry.port, | ||
| fingerprint: fpt, | ||
| connected: (ref4 = connects[regId]) != null ? ref4 : false, | ||
| aliases: entry.aliases, | ||
| vitae: entry.vitae | ||
| }); | ||
| } | ||
| return results; | ||
| })() | ||
| }); | ||
| }; | ||
| addAlias = function(regId, alias, cb) { | ||
| if (aliases[alias] != null) { | ||
| return cb("Alias " + alias + " already exists."); | ||
| } | ||
| if (data.registeredIds[regId] == null) { | ||
| return cb("No such registeredId " + regId + "."); | ||
| } | ||
| if (data.registeredIds[regId].aliases == null) { | ||
| data.registeredIds[regId].aliases = []; | ||
| } | ||
| data.registeredIds[regId].aliases.push(alias); | ||
| aliases[alias] = regId; | ||
| return write(cb); | ||
| }; | ||
| removeAlias = function(regId, alias, cb) { | ||
| if (data.registeredIds[regId] == null) { | ||
| return cb("No such registeredId " + regId + "."); | ||
| } | ||
| if (data.registeredIds[regId].aliases != null) { | ||
| data.registeredIds[regId].aliases = _.reject(data.registeredIds[regId].aliases, function(a) { | ||
| return a === alias; | ||
| }); | ||
| } | ||
| delete aliases[alias]; | ||
| return write(cb); | ||
| }; | ||
| sameKey = function(regIds, cb) { | ||
| var commonfpt, e, fpt, len2, m, pubkey; | ||
| commonfpt = null; | ||
| for (m = 0, len2 = regIds.length; m < len2; m++) { | ||
| regId = regIds[m]; | ||
| if ((entry = data.registeredIds[regId]) == null) { | ||
| return cb(null, false); | ||
| } | ||
| try { | ||
| pubkey = forge.pki.publicKeyFromPem(entry.key); | ||
| fpt = forge.ssh.getPublicKeyFingerprint(pubkey, { | ||
| encoding: 'hex', | ||
| delimiter: ':' | ||
| }); | ||
| } catch (error) { | ||
| e = error; | ||
| return cb(e); | ||
| } | ||
| if (commonfpt == null) { | ||
| commonfpt = fpt; | ||
| } else if (commonfpt !== fpt) { | ||
| return cb(null, false); | ||
| } | ||
| } | ||
| return cb(null, true); | ||
| }; | ||
| setVitae = function(regId, vitae, cb) { | ||
| if ((entry = data.registeredIds[regId]) == null) { | ||
| return cb("No such registeredId " + regId); | ||
| } | ||
| if (vitae != null) { | ||
| entry.vitae = vitae; | ||
| } else { | ||
| delete entry.vitae; | ||
| } | ||
| return write(cb); | ||
| }; | ||
| findAlias = function(alias, cb) { | ||
| return cb(null, aliases[alias]); | ||
| }; | ||
| isAvailable = function(regIds) { | ||
| var len2, m, ref3, takens; | ||
| takens = []; | ||
| for (m = 0, len2 = regIds.length; m < len2; m++) { | ||
| regId = regIds[m]; | ||
| if (!(((ref3 = connects[regId]) != null ? ref3 : false) === false)) { | ||
| takens.push(regId); | ||
| } | ||
| } | ||
| return takens; | ||
| }; | ||
| isConnected = function(regId) { | ||
| var ref3; | ||
| return ((ref3 = connects[regId]) != null ? ref3 : false) === true; | ||
| }; | ||
| connected = function(regId) { | ||
| return connects[regId] = true; | ||
| }; | ||
| connecting = function(regIds) { | ||
| var len2, m, results; | ||
| results = []; | ||
| for (m = 0, len2 = regIds.length; m < len2; m++) { | ||
| regId = regIds[m]; | ||
| results.push(connects[regId] = "connecting"); | ||
| } | ||
| return results; | ||
| }; | ||
| disconnected = function(regId) { | ||
| return connects[regId] = false; | ||
| }; | ||
| size = function(cb) { | ||
| return cb(null, _.keys(data.registeredIds).length); | ||
| }; | ||
| get = function(regId, cb) { | ||
| entry = data.registeredIds[regId]; | ||
| if ((entry == null) || (entry.port == null) || (entry.key == null)) { | ||
| return cb(new Error("No such registeredId \"" + regId + "\".")); | ||
| } | ||
| return cb(null, entry); | ||
| }; | ||
| find = function(regIds, cb) { | ||
| var key, len2, m, ports; | ||
| ports = []; | ||
| key = null; | ||
| for (m = 0, len2 = regIds.length; m < len2; m++) { | ||
| regId = regIds[m]; | ||
| entry = data.registeredIds[regId]; | ||
| if ((entry == null) || (entry.port == null) || (entry.key == null)) { | ||
| return cb(new Error("No such registeredId \"" + regId + "\".")); | ||
| } | ||
| if (key == null) { | ||
| key = entry.key; | ||
| } | ||
| ports.push(entry.port); | ||
| } | ||
| return cb(null, key, ports); | ||
| }; | ||
| each = function(icb) { | ||
| var fpt, key, len2, m, pubkey, ref3, results; | ||
| ref3 = _.keys(data.registeredIds); | ||
| results = []; | ||
| for (m = 0, len2 = ref3.length; m < len2; m++) { | ||
| key = ref3[m]; | ||
| entry = data.registeredIds[key]; | ||
| pubkey = forge.pki.publicKeyFromPem(entry.key); | ||
| fpt = forge.ssh.getPublicKeyFingerprint(pubkey, { | ||
| encoding: 'hex', | ||
| delimiter: ':' | ||
| }); | ||
| results.push(icb(null, { | ||
| fingerprint: fpt, | ||
| port: entry.port, | ||
| id: key, | ||
| aliases: entry.aliases | ||
| })); | ||
| } | ||
| return results; | ||
| }; | ||
| used = function(id, score, data, cb) { | ||
| return cb(null); | ||
| }; | ||
| usage = function(id, start, end, cb) { | ||
| return cb(null, [ | ||
| { | ||
| msg: "No in-memory usage" | ||
| } | ||
| ]); | ||
| }; | ||
| validatePreference = function(preferred) { | ||
| if ((preferred == null) || (preferred.id == null)) { | ||
| return false; | ||
| } | ||
| if (typeof preferred.id !== 'string') { | ||
| return false; | ||
| } | ||
| if (preferred.id.length < 4) { | ||
| return false; | ||
| } | ||
| if (preferred.port != null) { | ||
| if (!_.isInteger(preferred.port)) { | ||
| return false; | ||
| } | ||
| if (preferred.port < 11080) { | ||
| return false; | ||
| } | ||
| if (preferred.port > 91080) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| }; | ||
| register = function(pubkeypem, preferred, cb) { | ||
| var err, max, pubkey, registeredId, registeredPort, sshkey; | ||
| if ((data.maxRegisteredPort == null) && (data.lastPort != null)) { | ||
| data.maxRegisteredPort = data.lastPort; | ||
| } | ||
| if ((data.maxRegisteredId == null) && (data.lastRegisteredId != null)) { | ||
| data.maxRegisteredId = data.lastRegisteredId; | ||
| } | ||
| if (validatePreference(preferred)) { | ||
| if (!_.some(_.keys(data.registeredIds), function(k) { | ||
| return k === preferred.id; | ||
| })) { | ||
| registeredId = preferred.id; | ||
| } | ||
| if ((preferred.port != null) && !_.some(_.values(data.registeredIds), function(e) { | ||
| return e.port === preferred.port; | ||
| })) { | ||
| registeredPort = preferred.port; | ||
| } | ||
| } | ||
| if (registeredId == null) { | ||
| registeredId = (data.maxRegisteredId + 1).toString(); | ||
| } | ||
| if (registeredPort == null) { | ||
| registeredPort = data.maxRegisteredPort + 1; | ||
| } | ||
| try { | ||
| if (pubkeypem != null) { | ||
| pubkey = forge.pki.publicKeyFromPem(pubkeypem); | ||
| } | ||
| if (pubkey != null) { | ||
| sshkey = forge.ssh.publicKeyToOpenSSH(pubkey); | ||
| } | ||
| } catch (error) { | ||
| err = error; | ||
| return cb(err); | ||
| } | ||
| if (sshkey == null) { | ||
| return cb(new Error('Failed public key.')); | ||
| } | ||
| entry = data.registeredIds[registeredId] = { | ||
| port: registeredPort, | ||
| key: pubkeypem | ||
| }; | ||
| if (/^\d+$/.test(registeredId)) { | ||
| max = Math.max(data.maxRegisteredId, parseInt(registeredId)); | ||
| if (!isNaN(max)) { | ||
| data.maxRegisteredId = max; | ||
| } | ||
| } | ||
| data.maxRegisteredPort = Math.max(data.maxRegisteredPort, registeredPort); | ||
| return addAuthorized(sshkey, registeredPort, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return write(function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return cb(null, registeredId, entry); | ||
| }); | ||
| }); | ||
| }; | ||
| unregister = function(regIds, cb) { | ||
| return async.eachSeries(regIds, function(regId, cb) { | ||
| entry = data.registeredIds[regId]; | ||
| if (entry == null) { | ||
| return cb("No entry found for " + regId); | ||
| } | ||
| delete data.registeredIds[regId]; | ||
| if (_.keys(data.registeredIds).length === 0) { | ||
| data.maxRegisteredId = 1000; | ||
| data.maxRegisteredPort = 11080; | ||
| } | ||
| return removeAuthorized(entry.port, function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return write(function(err) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return cb(null, regId, entry); | ||
| }); | ||
| }); | ||
| }, cb); | ||
| }; | ||
| addAuthorized = function(sshkey, port, cb) { | ||
| return fs.appendFile(akPath, "command=\"echo 'This login is restricted.'\",no-agent-forwarding,no-X11-forwarding,permitopen=\"localhost:" + port + "\" " + sshkey + "\n", { | ||
| encoding: 'utf8' | ||
| }, cb); | ||
| }; | ||
| removeAuthorized = function(port, cb) { | ||
| return fs.readFile(akPath, { | ||
| encoding: 'utf8' | ||
| }, function(err, data) { | ||
| var lines, origLines; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| origLines = data.split('\n'); | ||
| lines = _.reject(origLines, function(l) { | ||
| return l.indexOf("localhost:" + port) > -1; | ||
| }); | ||
| return fs.writeFile(akPath, lines.join('\n'), { | ||
| encoding: 'utf8' | ||
| }, cb); | ||
| }); | ||
| }; | ||
| return { | ||
| setVitae: setVitae, | ||
| findAlias: findAlias, | ||
| addAlias: addAlias, | ||
| removeAlias: removeAlias, | ||
| status: status, | ||
| connected: connected, | ||
| connecting: connecting, | ||
| isAvailable: isAvailable, | ||
| isConnected: isConnected, | ||
| disconnected: disconnected, | ||
| size: size, | ||
| each: each, | ||
| first: first, | ||
| find: find, | ||
| get: get, | ||
| write: write, | ||
| register: register, | ||
| unregister: unregister, | ||
| sameKey: sameKey, | ||
| used: used, | ||
| usage: usage | ||
| }; | ||
| }; | ||
| }).call(this); |
| (function() { | ||
| var _, command, config, events, findCounts, findRegisters, findUsage, lock, net, say, scoring, sendTo, util, warn; | ||
| net = require('net'); | ||
| _ = require('lodash'); | ||
| events = require('events'); | ||
| config = require('../config'); | ||
| lock = require('../lock'); | ||
| util = require('./util'); | ||
| say = console.log; | ||
| warn = console.warn; | ||
| exports.start = function(port, opts, output) { | ||
| var allowCrossDomain, app, bodyParser, cmd, express; | ||
| express = require('express'); | ||
| bodyParser = require('body-parser'); | ||
| allowCrossDomain = function(req, res, next) { | ||
| var ref, ref1; | ||
| res.header('Access-Control-Allow-Credentials', true); | ||
| res.header('Access-Control-Allow-Origin', typeof req.headers === "function" ? req.headers(typeof origin !== "undefined" && origin !== null ? origin : '*') : void 0); | ||
| res.header('Access-Control-Allow-Methods', 'PUT,OPTIONS,GET,DELETE,POST'); | ||
| res.header('Access-Control-Allow-Headers', (ref = (ref1 = req.headers) != null ? ref1['Access-Control-Request-Headers'] : void 0) != null ? ref : 'x-requested-with,content-type,authorization,accept'); | ||
| res.header('Access-Control-Max-Age', 1728000); | ||
| return next(); | ||
| }; | ||
| app = express(); | ||
| app.use(allowCrossDomain); | ||
| app.use(bodyParser.json({ | ||
| limit: '1000kb' | ||
| })); | ||
| app.set('etag', false); | ||
| app.options('*', function(req, res, next) { | ||
| return res.send(200); | ||
| }); | ||
| cmd = command(opts, output); | ||
| app.get('/registry', cmd.registry); | ||
| app.get('/status', cmd.status); | ||
| app.get('/ping', cmd.ping); | ||
| app.get('/usage/:id', cmd.usage); | ||
| app.get('/stats/:id', cmd.stats); | ||
| app.get('/counts/registers', cmd.registers); | ||
| app.get('/counts/registrations', cmd.registrations); | ||
| app.get('/counts/errors', cmd.errors('errors')); | ||
| app.get('/counts/requests', cmd.errors('requests')); | ||
| app.listen(port); | ||
| return output.log('Server REST API started on', port); | ||
| }; | ||
| command = function(opts, output) { | ||
| return { | ||
| ping: function(req, res) { | ||
| var configPath, send; | ||
| send = sendTo(res); | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return lock.find('server', configPath, function(err, exists, piddata) { | ||
| if (err != null) { | ||
| return warn(err); | ||
| } | ||
| if (!exists) { | ||
| return send({ | ||
| status: 500, | ||
| error: "Server is not running." | ||
| }); | ||
| } | ||
| return send(null, { | ||
| success: { | ||
| success: true | ||
| } | ||
| }); | ||
| }); | ||
| }, | ||
| status: function(req, res) { | ||
| var configPath, send; | ||
| send = sendTo(res); | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return lock.find('server', configPath, function(err, exists, piddata) { | ||
| var buf, conn; | ||
| if (err != null) { | ||
| return warn(err); | ||
| } | ||
| if (!exists) { | ||
| return send({ | ||
| status: 500, | ||
| error: "Server is not running." | ||
| }); | ||
| } | ||
| conn = new net.Socket(); | ||
| conn.setEncoding('utf8'); | ||
| buf = ""; | ||
| conn.on('data', function(data) { | ||
| var c, downs, e, obj, ups; | ||
| try { | ||
| buf += data; | ||
| obj = JSON.parse(buf); | ||
| } catch (error) { | ||
| e = error; | ||
| say("...retrieving data..."); | ||
| return; | ||
| } | ||
| say("conns", obj.connections); | ||
| ups = (function() { | ||
| var i, len, ref, results; | ||
| ref = obj.connections; | ||
| results = []; | ||
| for (i = 0, len = ref.length; i < len; i++) { | ||
| c = ref[i]; | ||
| if (c.connected) { | ||
| results.push(c.registeredId); | ||
| } | ||
| } | ||
| return results; | ||
| })(); | ||
| downs = (function() { | ||
| var i, len, ref, results; | ||
| ref = obj.connections; | ||
| results = []; | ||
| for (i = 0, len = ref.length; i < len; i++) { | ||
| c = ref[i]; | ||
| if (!c.connected) { | ||
| results.push(c.registeredId); | ||
| } | ||
| } | ||
| return results; | ||
| })(); | ||
| return send(null, { | ||
| success: { | ||
| success: true, | ||
| total: ups.length + downs.length, | ||
| connected: ups, | ||
| disconnected: downs | ||
| } | ||
| }); | ||
| }); | ||
| return conn.connect(piddata.listen, 'localhost', function() { | ||
| return conn.write('STATUS\x03'); | ||
| }); | ||
| }); | ||
| }, | ||
| registry: function(req, res) { | ||
| var configPath, send; | ||
| send = sendTo(res); | ||
| configPath = config.pickConfig(opts.options.config); | ||
| return lock.find('server', configPath, function(err, exists, piddata) { | ||
| var buf, conn; | ||
| if (err != null) { | ||
| return warn(err); | ||
| } | ||
| if (!exists) { | ||
| return send({ | ||
| status: 500, | ||
| error: "Server is not running." | ||
| }); | ||
| } | ||
| conn = new net.Socket(); | ||
| conn.setEncoding('utf8'); | ||
| buf = ""; | ||
| conn.on('data', function(data) { | ||
| var e, obj; | ||
| try { | ||
| buf += data; | ||
| obj = JSON.parse(buf); | ||
| } catch (error) { | ||
| e = error; | ||
| say("...retrieving data..."); | ||
| return; | ||
| } | ||
| return send(null, { | ||
| success: obj | ||
| }); | ||
| }); | ||
| return conn.connect(piddata.listen, 'localhost', function() { | ||
| return conn.write('STATUS\x03'); | ||
| }); | ||
| }); | ||
| }, | ||
| usage: function(req, res) { | ||
| var end, regid, send, start; | ||
| send = sendTo(res); | ||
| regid = req.params.id; | ||
| start = req.query.start; | ||
| end = req.query.end; | ||
| return config.readConfig(opts.options.config, function(err, configPath, configData) { | ||
| if (err != null) { | ||
| return send("Error reading config \"" + configPath + "\": " + err); | ||
| } | ||
| return util.verifyConfig(configPath, configData, function(err) { | ||
| var ref; | ||
| if (err != null) { | ||
| return send("Can't verify server: " + err); | ||
| } | ||
| if ((ref = configData.server) != null) { | ||
| ref.configPath = configData.configPath; | ||
| } | ||
| return findUsage(regid, start, end, configData.server, function(err, data) { | ||
| if (err != null) { | ||
| return send("Error showing usage: " + err); | ||
| } | ||
| return send(null, { | ||
| success: data | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }, | ||
| stats: function(req, res) { | ||
| var end, regid, send, start; | ||
| send = sendTo(res); | ||
| regid = req.params.id; | ||
| start = req.query.start; | ||
| end = req.query.end; | ||
| return config.readConfig(opts.options.config, function(err, configPath, configData) { | ||
| if (err != null) { | ||
| return send("Error reading config \"" + configPath + "\": " + err); | ||
| } | ||
| return util.verifyConfig(configPath, configData, function(err) { | ||
| var ref; | ||
| if (err != null) { | ||
| return send("Can't verify server: " + err); | ||
| } | ||
| if ((ref = configData.server) != null) { | ||
| ref.configPath = configData.configPath; | ||
| } | ||
| return findUsage(regid, start, end, configData.server, function(err, data) { | ||
| var errors, successes, unavailables; | ||
| if (err != null) { | ||
| return send("Error showing usage: " + err); | ||
| } | ||
| successes = _.filter(data, { | ||
| success: true | ||
| }).length; | ||
| errors = _.filter(data, { | ||
| reason: 'service error' | ||
| }).length; | ||
| unavailables = _.filter(data, { | ||
| reason: 'service unavailable' | ||
| }).length; | ||
| return send(null, { | ||
| success: { | ||
| successes: successes, | ||
| errors: errors, | ||
| unavailables: unavailables | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }, | ||
| registrations: function(req, res) { | ||
| var end, send, start; | ||
| send = sendTo(res); | ||
| start = req.query.start; | ||
| end = req.query.end; | ||
| return config.readConfig(opts.options.config, function(err, configPath, configData) { | ||
| if (err != null) { | ||
| return send("Error reading config \"" + configPath + "\": " + err); | ||
| } | ||
| return util.verifyConfig(configPath, configData, function(err) { | ||
| var ref; | ||
| if (err != null) { | ||
| return send("Can't verify server: " + err); | ||
| } | ||
| if ((ref = configData.server) != null) { | ||
| ref.configPath = configData.configPath; | ||
| } | ||
| return configData.server.registry.size(function(err, sz) { | ||
| if (err != null) { | ||
| return send("Error finding registrations: " + err); | ||
| } | ||
| return send(null, { | ||
| success: { | ||
| count: sz | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }, | ||
| registers: function(req, res) { | ||
| var brand, end, send, start; | ||
| send = sendTo(res); | ||
| start = req.query.start; | ||
| end = req.query.end; | ||
| brand = req.headers['elements-g2c-brand'] || 'ce'; | ||
| return config.readConfig(opts.options.config, function(err, configPath, configData) { | ||
| if (err != null) { | ||
| return send("Error reading config \"" + configPath + "\": " + err); | ||
| } | ||
| return util.verifyConfig(configPath, configData, function(err) { | ||
| var ref; | ||
| if (err != null) { | ||
| return send("Can't verify server: " + err); | ||
| } | ||
| if ((ref = configData.server) != null) { | ||
| ref.configPath = configData.configPath; | ||
| } | ||
| return findRegisters(start, end, brand, configData.server, function(err, count) { | ||
| if (err != null) { | ||
| return send("Error showing registers: " + err); | ||
| } | ||
| return send(null, { | ||
| success: { | ||
| count: count | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }, | ||
| errors: function(type) { | ||
| return function(req, res) { | ||
| var brand, code, end, intype, ref, send, start; | ||
| send = sendTo(res); | ||
| start = req.query.start; | ||
| end = req.query.end; | ||
| brand = req.headers['elements-g2c-brand'] || 'ce'; | ||
| intype = (ref = req.query.type) != null ? ref : type; | ||
| code = req.query.code; | ||
| return config.readConfig(opts.options.config, function(err, configPath, configData) { | ||
| if (err != null) { | ||
| return send("Error reading config \"" + configPath + "\": " + err); | ||
| } | ||
| return util.verifyConfig(configPath, configData, function(err) { | ||
| var ref1; | ||
| if (err != null) { | ||
| return send("Can't verify server: " + err); | ||
| } | ||
| if ((ref1 = configData.server) != null) { | ||
| ref1.configPath = configData.configPath; | ||
| } | ||
| return findCounts(intype, code, start, end, brand, configData.server, function(err, count) { | ||
| if (err != null) { | ||
| return send("Error showing usage: " + err); | ||
| } | ||
| return send(null, { | ||
| success: { | ||
| count: count | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| } | ||
| }; | ||
| }; | ||
| findUsage = function(regid, start, end, csdata, cb) { | ||
| var ses; | ||
| ses = scoring(start, end); | ||
| return csdata.registry.usage(regid, ses[0], ses[1], cb); | ||
| }; | ||
| findRegisters = function(start, end, brand, csdata, cb) { | ||
| var ses; | ||
| ses = scoring(start, end); | ||
| return csdata.registry.registers(brand, ses[0], ses[1], cb); | ||
| }; | ||
| findCounts = function(type, code, start, end, brand, csdata, cb) { | ||
| var ses; | ||
| ses = scoring(start, end); | ||
| return csdata.registry.counts(type, code, brand, ses[0], ses[1], cb); | ||
| }; | ||
| scoring = function(start, end) { | ||
| var endScore, startScore; | ||
| endScore = end != null ? util.toScore(end) : util.nowScore(); | ||
| startScore = start != null ? util.toScore(start) : endScore - 2592000000; | ||
| return [startScore, endScore]; | ||
| }; | ||
| sendTo = function(res) { | ||
| return function(err, item) { | ||
| var ref; | ||
| if (err != null) { | ||
| if (err.status != null) { | ||
| res.status(err.status); | ||
| err = err.error; | ||
| } else { | ||
| res.status(500); | ||
| } | ||
| return res.send({ | ||
| message: 'Data Error', | ||
| error: err | ||
| }); | ||
| } else { | ||
| if (item == null) { | ||
| return res.status(404).send({ | ||
| message: 'Not found' | ||
| }); | ||
| } else if (item.failure != null) { | ||
| return res.status((ref = item.status) != null ? ref : 500).send(item.failure); | ||
| } else { | ||
| return res.send(item.success); | ||
| } | ||
| } | ||
| }; | ||
| }; | ||
| }).call(this); |
| (function() { | ||
| var backtalk, connectBacktalk, connectListener, connectProxy, events, listen, proxy, shutdown, start, waitfor; | ||
| events = require('events'); | ||
| listen = require("../listen"); | ||
| waitfor = require('../waitfor'); | ||
| backtalk = require("./backtalk"); | ||
| proxy = require("./proxy"); | ||
| exports.create = function() { | ||
| var protocol; | ||
| protocol = new events.EventEmitter(); | ||
| protocol.listener = new events.EventEmitter(); | ||
| protocol.backchannel = new events.EventEmitter(); | ||
| protocol.proxy = new events.EventEmitter(); | ||
| protocol._isShutdown = false; | ||
| protocol.end = function() { | ||
| if (protocol._isShutdown) { | ||
| return; | ||
| } | ||
| protocol._isShutdown = true; | ||
| return protocol.emit('shutdown'); | ||
| }; | ||
| protocol.status = function() { | ||
| return { | ||
| status: 'running' | ||
| }; | ||
| }; | ||
| protocol.start = function(configData) { | ||
| return start(protocol, configData); | ||
| }; | ||
| return protocol; | ||
| }; | ||
| start = function(protocol, configData) { | ||
| var waiter; | ||
| protocol.status = function(cb) { | ||
| return configData.registry.status(cb); | ||
| }; | ||
| protocol.statusOf = function(regId, cb) { | ||
| return configData.registry.statusOf(regId, cb); | ||
| }; | ||
| waiter = waitfor.create(function(isErr) { | ||
| return protocol.emit('close', isErr); | ||
| }); | ||
| return connectListener(protocol, configData, waiter); | ||
| }; | ||
| connectListener = function(protocol, configData, waiter) { | ||
| var lt; | ||
| lt = listen.create(); | ||
| lt.on('error', function(err) { | ||
| protocol.listener.emit('error', err); | ||
| protocol.emit('error', err); | ||
| return waiter.error(err); | ||
| }); | ||
| lt.on('close', function(hadErr) { | ||
| protocol.listener.emit('close', hadErr); | ||
| return waiter.close(); | ||
| }); | ||
| lt.on('connection', function(ltc) { | ||
| return protocol.listener.emit('connection', ltc); | ||
| }); | ||
| lt.on('command', function(cmd, ltc) { | ||
| protocol.listener.emit('command', cmd, ltc); | ||
| return protocol.emit('command', cmd, ltc); | ||
| }); | ||
| lt.on('listening', function(addr) { | ||
| waiter.open(); | ||
| protocol.listener.emit('listening', addr); | ||
| return connectBacktalk(protocol, lt, waiter.branch()); | ||
| }); | ||
| if (!protocol._isShutdown) { | ||
| protocol.on('shutdown', function() { | ||
| return shutdown(lt); | ||
| }); | ||
| return lt.listen(configData, 3016); | ||
| } else { | ||
| return waiter.close(); | ||
| } | ||
| }; | ||
| connectBacktalk = function(protocol, lt, waiter) { | ||
| var bt; | ||
| bt = backtalk.create(); | ||
| bt.on('error', function(err) { | ||
| protocol.backchannel.emit('error', err); | ||
| protocol.emit('error', err); | ||
| return waiter.error(err); | ||
| }); | ||
| bt.on('close', function(hadErr) { | ||
| protocol.backchannel.emit('close', hadErr); | ||
| shutdown(lt); | ||
| return waiter.close(); | ||
| }); | ||
| bt.on('connection', function(btc) { | ||
| return protocol.backchannel.emit('connection', btc); | ||
| }); | ||
| bt.on('listening', function(addr) { | ||
| waiter.open(); | ||
| protocol.backchannel.emit('listening', addr); | ||
| return connectProxy(protocol, lt, bt, waiter.branch()); | ||
| }); | ||
| lt.on('close', function() { | ||
| return shutdown(null, bt); | ||
| }); | ||
| if (!protocol._isShutdown) { | ||
| protocol.on('shutdown', function() { | ||
| return shutdown(null, bt); | ||
| }); | ||
| return bt.listen(lt); | ||
| } else { | ||
| return waiter.close(); | ||
| } | ||
| }; | ||
| connectProxy = function(protocol, lt, bt, waiter) { | ||
| var px; | ||
| px = proxy.create(); | ||
| px.on('error', function(err) { | ||
| protocol.proxy.emit('error', err); | ||
| protocol.emit('error', err); | ||
| return waiter.error(err); | ||
| }); | ||
| px.on('close', function(hadErr) { | ||
| protocol.proxy.emit('close', hadErr); | ||
| shutdown(lt, bt); | ||
| return waiter.close(); | ||
| }); | ||
| px.on('nokey', function() { | ||
| return protocol.proxy.emit('nokey'); | ||
| }); | ||
| px.on('connection', function(ltc, rid) { | ||
| return protocol.proxy.emit('connection', ltc, rid); | ||
| }); | ||
| px.on('listening', function() { | ||
| waiter.open(); | ||
| return protocol.proxy.emit('listening'); | ||
| }); | ||
| lt.on('close', function() { | ||
| return shutdown(null, null, px); | ||
| }); | ||
| bt.on('close', function() { | ||
| return shutdown(null, null, px); | ||
| }); | ||
| if (!protocol._isShutdown) { | ||
| protocol.on('shutdown', function() { | ||
| return shutdown(null, null, px); | ||
| }); | ||
| return px.listen(lt, bt); | ||
| } else { | ||
| return waiter.close(); | ||
| } | ||
| }; | ||
| shutdown = function(lt, bt, px) { | ||
| if (px != null) { | ||
| px.end(); | ||
| } | ||
| if (bt != null) { | ||
| bt.end(); | ||
| } | ||
| return lt != null ? lt.end() : void 0; | ||
| }; | ||
| }).call(this); |
| (function() { | ||
| var _, akPath, async, forge, fs, home, path, rclient, redis, regUrl, shPath; | ||
| _ = require('lodash'); | ||
| redis = require('redis'); | ||
| async = require('async'); | ||
| path = require('path'); | ||
| forge = require('node-forge'); | ||
| fs = require('fs'); | ||
| regUrl = 'redis://localhost:6379'; | ||
| rclient = redis.createClient(regUrl); | ||
| home = process.env.HOME || process.env.USERPROFILE; | ||
| shPath = path.join(home, '.ssh'); | ||
| akPath = path.join(shPath, 'authorized_keys.new'); | ||
| rclient.smembers('g2c/allids', function(err, ids) { | ||
| return async.each(ids, function(id, cb) { | ||
| return rclient.get("g2c/ids/" + id + "/port", function(err, port) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| return rclient.get("g2c/ids/" + id + "/key", function(err, pubkeypem) { | ||
| var pubkey, sshkey; | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| try { | ||
| pubkey = forge.pki.publicKeyFromPem(pubkeypem); | ||
| sshkey = forge.ssh.publicKeyToOpenSSH(pubkey); | ||
| } catch (error) { | ||
| err = error; | ||
| return cb("problem on " + id + ": " + err); | ||
| } | ||
| return fs.appendFile(akPath, "command=\"echo 'This login is restricted.'\",no-agent-forwarding,no-X11-forwarding,permitopen=\"localhost:" + port + "\" " + sshkey + "\n", { | ||
| encoding: 'utf8' | ||
| }, cb); | ||
| }); | ||
| }); | ||
| }, function(err) { | ||
| if (err != null) { | ||
| console.warn(err); | ||
| } | ||
| console.log('done'); | ||
| return rclient.quit(); | ||
| }); | ||
| }); | ||
| }).call(this); |
| (function() { | ||
| var guid, patches, registry, verifyConfig; | ||
| registry = require('./registry_redis'); | ||
| patches = require('./patches'); | ||
| exports.guid = guid = function() { | ||
| var s4; | ||
| s4 = function() { | ||
| return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); | ||
| }; | ||
| return s4() + s4() + s4() + s4(); | ||
| }; | ||
| exports.verifyConfig = verifyConfig = function(configPath, configData, cb) { | ||
| return registry.manage(configPath, configData, function(err, registry) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| configData.server.registry = registry; | ||
| return patches.manage(configPath, configData, function(err, patches) { | ||
| if (err != null) { | ||
| return cb(err); | ||
| } | ||
| configData.server.patches = patches; | ||
| return cb(null); | ||
| }); | ||
| }); | ||
| }; | ||
| exports.toScore = function(val) { | ||
| return new Date(val).getTime(); | ||
| }; | ||
| exports.nowScore = function() { | ||
| return new Date().getTime(); | ||
| }; | ||
| }).call(this); |
| (function() { | ||
| var stream; | ||
| stream = require('stream'); | ||
| exports.take = function(base, n, done, dstat) { | ||
| var ended, fin, rem, wrap, wrapread; | ||
| rem = n; | ||
| wrap = stream.Readable(); | ||
| ended = false; | ||
| if (done != null) { | ||
| wrap.once('end', done); | ||
| } | ||
| base.once('end', function() { | ||
| ended = true; | ||
| return fin(); | ||
| }); | ||
| fin = function() { | ||
| wrap.push(null); | ||
| return base.removeListener('readable', wrapread); | ||
| }; | ||
| wrap._read = function(size) { | ||
| var data; | ||
| if (ended || rem <= 0) { | ||
| return; | ||
| } | ||
| if (size != null) { | ||
| size = Math.min(size, rem); | ||
| } else { | ||
| size = rem; | ||
| } | ||
| data = base.read(size); | ||
| if (data == null) { | ||
| data = base.read(); | ||
| } | ||
| if (data != null) { | ||
| rem -= data.length; | ||
| wrap.push(data); | ||
| if (typeof dstat === "function") { | ||
| dstat(n - rem, n); | ||
| } | ||
| if (rem <= 0) { | ||
| return fin(); | ||
| } | ||
| } | ||
| }; | ||
| wrapread = function() { | ||
| return wrap._read(); | ||
| }; | ||
| base.on('readable', wrapread); | ||
| return wrap; | ||
| }; | ||
| }).call(this); |
| (function() { | ||
| var create, createFake, | ||
| slice = [].slice; | ||
| exports.create = create = function(cb) { | ||
| var children, complete, completed, opened, savedArgs, savedErr; | ||
| opened = false; | ||
| completed = false; | ||
| children = 0; | ||
| savedErr = false; | ||
| savedArgs = []; | ||
| complete = function() { | ||
| var args, isErr; | ||
| isErr = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; | ||
| completed = true; | ||
| savedErr = isErr; | ||
| savedArgs = args; | ||
| if (children === 0) { | ||
| return cb.apply(null, [isErr].concat(slice.call(args))); | ||
| } | ||
| }; | ||
| return { | ||
| error: function() { | ||
| var args; | ||
| args = 1 <= arguments.length ? slice.call(arguments, 0) : []; | ||
| if (completed || opened) { | ||
| return; | ||
| } | ||
| return complete.apply(null, [true].concat(slice.call(args))); | ||
| }, | ||
| open: function() { | ||
| if (completed) { | ||
| return; | ||
| } | ||
| return opened = true; | ||
| }, | ||
| close: function() { | ||
| var args; | ||
| args = 1 <= arguments.length ? slice.call(arguments, 0) : []; | ||
| if (completed) { | ||
| return; | ||
| } | ||
| return complete.apply(null, [false].concat(slice.call(args))); | ||
| }, | ||
| branch: function() { | ||
| var child; | ||
| if (completed || !opened) { | ||
| return createFake(); | ||
| } | ||
| ++children; | ||
| child = create(function(isErr) { | ||
| --children; | ||
| savedErr = savedErr || isErr; | ||
| if (completed && children === 0) { | ||
| return cb.apply(null, [savedErr].concat(slice.call(savedArgs))); | ||
| } | ||
| }); | ||
| return child; | ||
| } | ||
| }; | ||
| }; | ||
| createFake = function() { | ||
| return { | ||
| error: function() {}, | ||
| open: function() {}, | ||
| close: function() {}, | ||
| branch: function() { | ||
| return createFake(); | ||
| } | ||
| }; | ||
| }; | ||
| }).call(this); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Unidentified License
LicenseSomething that seems like a license was found, but its contents could not be matched with a known license.
Network access
Supply chain riskThis module accesses the network.
Found 3 instances
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
1
-96.67%1
-93.33%119392
-68.63%16
-67.35%1
Infinity%80
-20%212
-97.64%+ Added
+ Added
- Removed
Updated