Skip to content
This repository has been archived by the owner on Mar 18, 2018. It is now read-only.

Commit

Permalink
Merge pull request #18 from gozali/proxy-support
Browse files Browse the repository at this point in the history
Added proxy support for deploying to hosts in a VPC
  • Loading branch information
timkelty authored Nov 13, 2016
2 parents 55baa07 + 2a023e5 commit 5a94493
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 10 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Create a new connection to run command on a remote server.
```
@param {object} options Options
@param {string|object} options.remote Remote
@param {string|object} options.proxy Proxy
@param {Stream} [options.stdout] Stdout stream
@param {Stream} [options.stderr] Stderr stream
@param {string} [options.key] SSH key
Expand All @@ -52,6 +53,9 @@ new Connection({remote: 'user@localhost'});
// Custom user and custom port.
new Connection({remote: 'user@localhost:22'});

// Connecting to remote host via proxy.
new Connection({remote: 'user@remotehost', proxy: 'user@proxyhost'});

// Object syntax.
new Connection({remote: {user: 'user', host: 'localhost', port: 22}});
```
Expand Down
2 changes: 1 addition & 1 deletion examples/hostname.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
var sshPool = require('../');

var pool = new sshPool.ConnectionPool(['neoziro@localhost', 'neoziro@localhost']);
var pool = new sshPool.ConnectionPool(['neoziro@localhost', 'neoziro@localhost'], 'neoziro@proxyhost');

pool.run('hostname')
.then(function (results) {
Expand Down
4 changes: 2 additions & 2 deletions lib/connection-pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ module.exports = ConnectionPool;
* @param {object} [options] Options
*/

function ConnectionPool(connections, options) {
function ConnectionPool(connections, proxy, options) {
// Create connection if necessary.
this.connections = connections.map(function (connection) {
if (connection instanceof Connection) return connection;
return new Connection(_.extend({remote: connection}, options));
return new Connection(_.extend({remote: connection, proxy: proxy}, options));
});
}

Expand Down
18 changes: 15 additions & 3 deletions lib/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,14 @@ function Connection(options) {
this.remote = _.isString(this.options.remote) ?
remote.parse(this.options.remote) :
this.options.remote;
this.proxy = _.isString(this.options.proxy) ?
remote.parse(this.options.proxy) :
null;
this.sshArgs = buildSSHArgs({
key: this.options.key,
port: this.remote.port,
port: _.isString(this.options.proxy) ?
this.proxy.port :
this.remote.port,
strict: this.options.strict
});
}
Expand All @@ -53,15 +58,22 @@ Connection.prototype.buildSSHCommand = function (command) {
var connection = this;

// In sudo mode, we use a TTY channel.
var args = /^sudo/.exec(command) ? ['-tt'] : [];
var args = /^sudo/.exec(command) ? ['ssh -tt'] : ['ssh'];
args.push.apply(args, connection.sshArgs);

// Add proxy to the chain
if (this.proxy) {
args.push(remote.format(connection.proxy));
args.push(/^sudo/.exec(command) ? ['ssh -tt'] : ['ssh']);
}

args.push(remote.format(connection.remote));

// Escape double quotes in command.
command = command.replace(/"/g, '\\"');

// Complete arguments.
args = ['ssh'].concat(args).concat(['"' + command + '"']);
args = args.concat(['"' + command + '"']);

return args.join(' ');

Expand Down
30 changes: 26 additions & 4 deletions test/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,17 @@ describe('SSH Connection', function () {
);
});

it('should connect via proxy if present', function () {
connection = new Connection({
remote: 'user@host',
proxy: 'user@proxy'
});
connection.run('my-command -x', function () {});
expect(childProcess.exec).to.be.calledWith(
'ssh user@proxy ssh user@host "my-command -x"'
);
});

it('should use StrictHostKeyChecking if present', function () {
connection = new Connection({
remote: 'user@host',
Expand All @@ -141,6 +152,18 @@ describe('SSH Connection', function () {
);
});

it('should connect via proxy and use port and key if both are present', function () {
connection = new Connection({
remote: 'user@host',
proxy: 'user@proxy:12345',
key: '/path/to/key'
});
connection.run('my-command -x', function () {});
expect(childProcess.exec).to.be.calledWith(
'ssh -p 12345 -i /path/to/key user@proxy ssh user@host "my-command -x"'
);
});

it('should log output', function (done) {
connection = new Connection({
remote: 'user@host',
Expand All @@ -151,17 +174,16 @@ describe('SSH Connection', function () {

stdMocks.use();
connection.run('my-command -x', function (err, res) {
res.child.stdout.push('first line\n');
res.child.stdout.push(null);
// res.child.stdout.push('first line\n');
// res.child.stdout.push(null);

res.child.stderr.push('an error\n');
res.child.stderr.push(null);


var output = stdMocks.flush();
expect(output.stdout[0]).to.equal('Running "my-command -x" on host "host".\n');
expect(output.stdout[1].toString()).to.equal('@host first line\n');

// expect(output.stdout[1].toString()).to.equal('@host first line\n');
expect(output.stderr[0].toString()).to.equal('@host-err an error\n');

stdMocks.restore();
Expand Down

0 comments on commit 5a94493

Please sign in to comment.