Skip to content

Instantly share code, notes, and snippets.

Created December 13, 2012 20:08
Show Gist options
  • Save anonymous/4279365 to your computer and use it in GitHub Desktop.
Save anonymous/4279365 to your computer and use it in GitHub Desktop.
{
"key": "/home/me/foo_key",
"user": "foo",
"targets": {
"all": [
"example.org#Default user, key auth, port 22",
"root@example.net:2233#Root user, key auth, port 2233",
"root:toor@example.com#Root user, password auth, port 22"
],
"production": [
"root:toor@example.com#Root user, password auth, port 22"
],
"staging": [
"root@example.net:2233#Root user, key auth, port 2233"
],
"dev": [
"example.org#Default user, key auth, port 22"
]
}
}
var fs = require('fs'),
Connection = require('ssh2');
var config = JSON.parse(fs.readFileSync('config.json', 'utf8')),
key, user, conntimeout = 60000, targets, i, len, exec;
if (typeof config.key === 'string' && config.key.length)
key = fs.readFileSync(config.key);
if (typeof config.conntimeout === 'number')
conntimeout = config.conntimeout;
if (typeof config.user === 'string' && config.user.length)
user = config.user;
if (process.argv.length < 3)
throw new Error('Missing target group');
else if (process.argv.length < 4)
throw new Error('Missing exec string');
if (typeof config.targets !== 'object')
throw new Error('No target groups defined');
if (!Array.isArray(config.targets[process.argv[2]]))
throw new Error('No list for that target group');
targets = config.targets[process.argv[2]];
exec = process.argv[3];
if (targets.length === 0)
throw new Error('No targets in list');
// [ user, password, host, port, label ]
var reTarget = /^(?:([^:@]+?)(?::([^@]+))?@)?([A-Za-z0-9](?:[\w\.\-]+)?)(?::(\d+))?(?:#(.+))?$/;
function parseTarget(t) {
var info = {
name: undefined,
didExit: false,
conn: undefined,
host: undefined,
port: 22,
user: undefined,
password: undefined,
label: undefined
};
var m = reTarget.exec(t);
if (!m)
throw new Error('Unable to parse target: ' + t);
if (!key && !m[2])
throw new Error('No way to authenticate for target: ' + t);
info.user = m[1];
info.password = m[2];
info.host = m[3];
if (m[4])
info.port = parseInt(m[4], 10);
info.label = m[5];
return info;
}
for (i = 0, len = targets.length; i < len; ++i)
targets[i] = parseTarget(targets[i]);
var togo = targets.length,
noexec = [];
targets.forEach(function(target) {
target.name = '[' + target.host;
target.didExit = false;
if (target.label)
target.name += ' (' + target.label + ')';
target.name += ']';
target.conn = new Connection();
target.conn.connected = false;
target.conn.timeout = setTimeout(function() {
if (!target.conn.connected) {
console.log(target.name + ' Connection timeout');
target.conn.end();
}
}, conntimeout);
target.conn.on('connect', function() {
clearTimeout(target.conn.timeout);
});
target.conn.on('ready', function() {
target.conn.exec(exec, function(err, stream) {
if (err) {
console.log(target.name + ' Error during exec: ' + err);
return target.conn.end();
}
stream.on('exit', function(code, signal) {
target.didExit = true;
if (signal)
console.log(target.name + ' Exited with signal ' + signal);
else
console.log(target.name + ' Exited with code ' + code);
target.conn.end();
});
});
});
target.conn.on('error', function(err) {
console.log(target.name + ' Connection error: ' + err);
});
target.conn.on('close', function(had_err) {
if (!target.didExit)
noexec.push(target.name);
console.log(target.name + ' Connection closed (' + (--togo) + ' to go)');
});
if (target.password) {
target.conn.connect({
host: target.host,
port: target.port,
username: target.user || user,
password: target.password
});
} else {
target.conn.connect({
host: target.host,
port: target.port,
username: target.user || user,
privateKey: key
});
}
});
process.on('exit', function() {
if (noexec.length) {
console.log('The following servers failed to exec (for any reason):');
console.dir(noexec);
}
});
# Usage examples
# execute `uptime` on all systems
node exec all "uptime"
# example output (example.net host is down):
[example.org (Default user, key auth, port 22)] Exited with code 0
[example.org (Default user, key auth, port 22)] Connection closed (2 to go)
[example.net (Root user, key auth, port 2233)] Connection closed (1 to go)
[example.com (Root user, password auth, port 22)] Exited with code 0
[example.com (Root user, password auth, port 22)] Connection closed (0 to go)
The following servers failed to exec (for any reason):
[ '[example.net (Root user, key auth, port 2233)]' ]
# install node v0.8.16 on dev systems
node exec dev "mkdir -p /tmp/nodeinstall && cd /tmp/nodeinstall && rm -rf node-* && curl http://nodejs.org/dist/latest/node-v0.8.16.tar.gz | tar zx && cd node-v0.8.16 && ./configure && make && make install"
# example output:
[example.org (Default user, key auth, port 22)] Exited with code 0
[example.org (Default user, key auth, port 22)] Connection closed (2 to go)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment