UNPKG

the-shepherd

Version:
258 lines (237 loc) 8.01 kB
// Generated by CoffeeScript 2.5.1 (function() { //!/usr/bin/env coffee var $, Actions, Daemon, Fs, Net, __VERSION__, _on, basePath, c, carefulUnlink, cmd, configFile, createBasePath, die_soon, doHelp, doInit, echo, exists, exit_soon, expandPath, ignore, isPidAlive, pidFile, readPid, readTimeout, sendServerCmd, socketFile, startupTimeout, statusChangeCommands, verbose, waitForSocket, warn, indexOf = [].indexOf; __VERSION__ = '0.3.29'; Fs = require('fs'); Net = require('net'); Daemon = require('../daemon'); ({Actions} = require('../actions')); ({$, cmd, echo, warn, verbose, exit_soon, die_soon} = require('../common')); ({exists, pidFile, socketFile, configFile, basePath, createBasePath, expandPath, readPid, carefulUnlink} = require('../files')); ({isPidAlive} = require('../util/process-slim')); readTimeout = 3000; // how long to wait for a response, to any command startupTimeout = 1000; // how long to wait after issuing an 'up' before inquiring with 'status' ignore = function() {}; doInit = function(cb) { var defaultsFile; defaultsFile = process.cwd() + "/.shep/defaults"; cb || (cb = ignore); if (exists(configFile) && !(cmd.f || cmd.force)) { echo(`Configuration already exists (${configFile})`); cb(null, false); } else { echo("Initializing .shep/config..."); if (exists(defaultsFile)) { echo("Applying default config...", configFile); Fs.copyFile(expandPath(defaultsFile), expandPath(configFile), (err) => { return cb(null, true); }); } else { createBasePath(".", cb); } } return null; }; _on = (s, kv) => { var k, results, v; results = []; for (k in kv) { v = kv[k]; results.push(s.on(k, v.bind(s))); } return results; }; sendServerCmd = (_cmd, cb) => { var Tnet, action, on_error, retryConnect, savedPid; if (!exists(basePath)) { return echo("No .shep directory found."); } if (!exists(socketFile)) { // If there's no socket to connect to, but a PID is still alive, try to kill it via signal if (exists(expandPath(pidFile))) { warn("Stale PID file detected:", pidFile); isPidAlive(savedPid = readPid(), function(err, alive) { if (alive) { warn(`PID (${savedPid}) still running, but not listening on a socket, attempt to kill it.`); return process.kill(savedPid, "SIGTERM"); } else { return carefulUnlink(pidFile, function(err) { if (err) { return warn(`client/index Error when unlinking PID file (${pidFile}):`, err); } }); } }); } return die_soon("Status: offline (no socket file).", 3); } cb || (cb = ignore); if (!(action = Actions[_cmd])) { return warn("No such action:", _cmd); } Net = require('net'); Tnet = require('../util/tnet'); on_error = (err) => { console.error("on_error:", err); return warn("socket error", $.debugStack(err)); }; (retryConnect = () => { _on(Net.connect({ path: expandPath(socketFile) }), { close: function() { return cb(null, true); }, error: function(err) { var ref; if (err.code === 'ENOENT') { if (_cmd === 'start') { Daemon.doStart(false); return setTimeout(retryConnect, readTimeout); } else { return die_soon(`Status: offline (${err.code}, ${_cmd}).`, 3); } } else if ((ref = err.code) === 'EADDRNOTAVAIL' || ref === 'ECONNREFUSED') { return die_soon(`Status: offline (${err.code}).`, 3); } else { return on_error(err); } }, connect: function() { var bytes, err, msg; try { msg = action.toMessage(cmd); if (cmd._.length > 1 && !(('group' in cmd) || ('instance' in cmd))) { if (/\w+/.test(cmd._[1])) { msg.auto = cmd._[1]; } } bytes = $.TNET.stringify(msg); } catch (error) { err = error; return on_error(err); } // Send the command bytes to the server this.write(bytes, () => { var perItem, timeout; if (typeof action.onConnect === "function") { action.onConnect(this); } if (!('onResponse' in action)) { return this.end(); } // If there's an onResponse, read the timeout = $.delay(readTimeout, () => { warn("Timed-out waiting for a response from the daemon."); return this.end(); }); perItem = (item) => { return action.onResponse(item, this); }; Tnet.read_stream(this, perItem).then(() => { timeout.cancel(); return this.end(); }); return null; }); return null; } }); return null; })(); return null; }; waitForSocket = (timeout, cb) => { // wait for the daemon to connect to the other side of the socketFile var poll, start; start = +new Date(); return (poll = () => { var elapsed; if ((elapsed = +new Date() - start) > timeout) { return cb('timeout'); } if (!exists(socketFile)) { return setTimeout(poll, 100); } return _on(Net.connect({ path: expandPath(socketFile) }), { error: function(err) { this.end(); // close our side verbose("Ignoring error while waiting:", err); return setTimeout(poll, 100); // retry }, connect: function() { this.end(); // close our side, its just a probe return cb(null); } }); })(); }; doHelp = () => { var k, o; console.log(cmd._[1] in Actions ? `shep ${cmd._[1]}\n ` + ((function() { var i, len, ref, ref1, results; ref1 = (ref = Actions[cmd._[1]].options) != null ? ref : [["No options."]]; results = []; for (i = 0, len = ref1.length; i < len; i++) { o = ref1[i]; results.push(o.join(' - ')); } return results; })()).join("\n ") : `shep <${((function() { var results; results = []; for (k in Actions) { results.push(k); } return results; })()).join("|")}>`); return process.exit(0); }; // the subset of commands that cause status changes (and should show status) statusChangeCommands = ['start', 'stop', 'enable', 'disable', 'add', 'remove', 'scale', 'replace']; c = cmd._[0]; switch (c) { // some commands get handled without connecting to the daemon case 'help': doHelp(); break; case 'version': console.log(__VERSION__); break; case 'init': doInit((err) => { err && warn(err); return exit_soon((err ? 1 : 0)); }); break; case 'up': Daemon.doStart(false); waitForSocket(5000, (err) => { if (err === 'timeout') { warn("Daemon did not start within timeout."); return exit_soon(1); } else { return sendServerCmd('status', () => { return exit_soon(0); }); } }); break; default: // disabled special 'down' case, so that we create a 'down' message in the 'else' case below // when 'down' then Daemon.doStop(true) # this is now called from actions/down sendServerCmd(c, () => { if (indexOf.call(statusChangeCommands, c) >= 0) { return $.delay(500, () => { return sendServerCmd('status', () => { return exit_soon(0); }); }); } else { return exit_soon(0); } }); } }).call(this);