domotz-remote-pawn
Version:
Domotz Agent
176 lines (155 loc) • 5.96 kB
JavaScript
/** This file is part of Domotz Agent.
* Copyright (C) 2016 Domotz Ltd
*
* Domotz Agent is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Domotz Agent is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Domotz Agent. If not, see <http://www.gnu.org/licenses/>.
*
* Created by Tommaso Latini <tommaso@domotz.com> on 20/09/16.
*/
const PROMISE = 0;
const COMMAND = 1;
const ARGS = 2;
const OPTIONS = 3;
const SPAWN_HARD_TIMEOUT = 180000;
var startTime;
module.exports = function (index, childProcess, lodash, myConsole) {
var prefix = 'EXECUTOR - (' + index + '):',
queue = [],
current = null;
function describe(command, args, options) {
return command + (args ? ' ' + args.join(' ') + '' : '') + (options ? '. Options: ' + JSON.stringify(options) : '');
}
function finalize(id) {
clearTimeout(id);
dequeue();
}
function resolve(deferred, response, id) {
deferred.resolve(response);
finalize(id);
}
function reject(deferred, error, id) {
deferred.reject(error);
finalize(id);
}
function enqueue(task) {
queue.push(task);
if (!current) {
dequeue();
}
}
function dequeue() {
current = queue.shift();
if (!current) {
myConsole.verbose('%s Empty queue.', prefix);
return;
}
var options = current[OPTIONS] || {};
var timeOut;
if (options.timeout === undefined && options.ttl === undefined) {
timeOut = SPAWN_HARD_TIMEOUT;
} else {
timeOut = options.timeout || options.ttl || null;
}
var deferred = current[PROMISE],
command = current[COMMAND],
args = current[ARGS] || [],
description = (options && options.description) || describe(command, args, options),
id = null,
err = null,
stdout = '',
stderr = '',
pid = undefined,
child = undefined;
try {
startTime = new Date();
child = childProcess.spawn(command, args, options);
pid = child.pid;
} catch (err) {
myConsole.warn('%s Process pid:%s "%s" exited with unexpected error', prefix, pid, description);
reject(deferred, err.stack, undefined);
return;
}
if (child) {
if (timeOut) {
myConsole.debug('%s pid:%s Set timeOut %s to %s', prefix, pid, timeOut, description);
id = setTimeout(function () {
myConsole.debug('Timeout expired for pid:%s', pid);
child.kill();
err = new Error(prefix + ' - Process "' + description + '" killed after timeout: ' + timeOut);
reject(deferred, err, id);
}, timeOut);
} else {
myConsole.info('%s Spawned child pid: %s. Path: %s', prefix, pid, description);
}
if (child.stdout) {
child.stdout.on('data', function (data) {
stdout += data;
if (options.onStdout) {
options.onStdout(data, deferred);
}
});
}
if (child.stderr) {
child.stderr.on('data', function (data) {
stderr += data;
if (options.onStderr) {
options.onStderr(data, deferred);
}
});
}
child.on('error', function (err) {
myConsole.warn('%s Process pid:%s "%s" exited with unexpected error', prefix, pid, description);
reject(deferred, err, id);
});
child.on('close', function (code) {
var duration = new Date() - startTime;
myConsole.debug('%s Process with pid:%s exited after %dms', prefix, pid, duration);
if (stdout.length > 0) {
myConsole.verbose('%s pid:%s STDOUT: %s ', prefix, pid, stdout);
}
if (stderr.length > 0) {
myConsole.verbose('%s pid:%s STDERR: %s ', prefix, pid, stderr);
}
if (code !== 0 || (stderr && !stdout)) {
stderr = stderr.toString().trim();
err = new Error(stderr);
err.code = code;
err.message = stderr || stdout;
myConsole.error('%s Process with pid:%s "%s" exited with code: %s. "%s"', prefix, pid, description, code, stderr);
reject(deferred, err, id);
} else {
stdout = stdout.toString().trim();
resolve(deferred, stdout, id);
}
});
}
}
function status() {
var length = queue.length;
if (current) {
var processTimeout = lodash.get(current, '[' + OPTIONS + '].timeout', undefined);
if (processTimeout === null) {
myConsole.debug('%s Executor %s is busy with long running process', prefix, index);
return null;
}
}
return current ? length + 1 : length;
}
function getIndex() {
return index;
}
this.enqueue = enqueue;
this.dequeue = dequeue;
this.status = status;
this.getIndex = getIndex;
};