Created
December 13, 2012 20:08
-
-
Save anonymous/4279365 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"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" | |
] | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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