Skip to content
This repository was archived by the owner on May 31, 2022. It is now read-only.

Commit 299b632

Browse files
gribnoysupaddaleax
andauthored
fix: Wait for the tunnel to close before finishing client close COMPASS-4474 (#352)
* fix: Wait for the tunnel to close before finishing client close * fix: Update ssh-tunnel mock to match new implementation * fix: Only pick up errors that originated in ssh-client * fix: Increase the timeout to make sure tunnel has a chance to start (and fail) before server stops trying * refactor: Async-ify create tunnel and connect tasks; Expose tunnel through connect instead of overriding client.close method * refactor: Pass tunnel as function parameter * fix: Bump ssh-tunnel version once more * refactor: Asyncify awaiting on tunnel error Co-authored-by: Anna Henningsen <anna@addaleax.net> * fix: Change spied close method Co-authored-by: Anna Henningsen <anna@addaleax.net>
1 parent 6bf4c43 commit 299b632

File tree

7 files changed

+113
-660
lines changed

7 files changed

+113
-660
lines changed

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"editor.formatOnSave": true,
2+
"editor.formatOnSave": false,
33
"editor.tabSize": 2,
44
"editor.insertSpaces": true,
55
"files.trimTrailingWhitespace": true,

lib/connect.js

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { EventEmitter } = require('events');
1+
const { EventEmitter, once } = require('events');
22
const fs = require('fs');
33
const async = require('async');
44
const {
@@ -12,7 +12,7 @@ const {
1212
const { MongoClient } = require('mongodb');
1313
const { parseConnectionString } = require('mongodb/lib/core');
1414
const Connection = require('./extended-model');
15-
const createSSHTunnel = require('./ssh-tunnel');
15+
const { default: SSHTunnel } = require('@mongodb-js/ssh-tunnel');
1616

1717
const debug = require('debug')('mongodb-connection-model:connect');
1818

@@ -125,8 +125,9 @@ const getTasks = (model, setupListeners) => {
125125
const tasks = {};
126126
const _statuses = {};
127127
let options = {};
128-
let tunnel;
129-
let client;
128+
/** @type {SSHTunnel} */
129+
let tunnel = null;
130+
let client = null;
130131

131132
const status = (message, cb) => {
132133
if (_statuses[message]) {
@@ -189,19 +190,27 @@ const getTasks = (model, setupListeners) => {
189190
});
190191

191192
assign(tasks, {
192-
[Tasks.CreateSSHTunnel]: (cb) => {
193-
const ctx = status('Create SSH Tunnel', cb);
193+
[Tasks.CreateSSHTunnel]: async() => {
194+
const ctx = status('Create SSH Tunnel');
194195

195196
if (model.sshTunnel === 'NONE') {
196197
return ctx.skip('The selected SSH Tunnel mode is NONE.');
197198
}
198199

199-
tunnel = createSSHTunnel(model, ctx);
200+
tunnel = new SSHTunnel(model.sshTunnelOptions);
201+
202+
try {
203+
await tunnel.listen();
204+
ctx(null);
205+
} catch (err) {
206+
ctx(err);
207+
throw err;
208+
}
200209
}
201210
});
202211

203212
assign(tasks, {
204-
[Tasks.ConnectToMongoDB]: (cb) => {
213+
[Tasks.ConnectToMongoDB]: async() => {
205214
const ctx = status('Connect to MongoDB');
206215

207216
// @note: Durran:
@@ -215,6 +224,7 @@ const getTasks = (model, setupListeners) => {
215224

216225
validOptions.useNewUrlParser = true;
217226
validOptions.useUnifiedTopology = true;
227+
218228
if (
219229
model.directConnection === undefined &&
220230
model.hosts.length === 1 &&
@@ -234,29 +244,37 @@ const getTasks = (model, setupListeners) => {
234244
setupListeners(mongoClient);
235245
}
236246

237-
mongoClient.connect((err, _client) => {
238-
ctx(err);
239-
240-
if (err) {
241-
if (tunnel) {
242-
debug('data-service connection error, shutting down ssh tunnel');
243-
tunnel.close();
247+
/** @type {Promise<never>} */
248+
const waitForTunnelError = (async() => {
249+
const [error] = await once(tunnel || new EventEmitter(), 'error');
250+
throw error;
251+
})();
252+
253+
const closeTunnelOnError = async(tunnelToClose) => {
254+
if (tunnelToClose) {
255+
debug('data-service connection error, shutting down ssh tunnel');
256+
try {
257+
await tunnelToClose.close();
258+
debug('ssh tunnel stopped');
259+
} catch (err) {
260+
debug('ssh tunnel stopped with error: %s', err.message);
244261
}
245-
246-
return cb(err);
247262
}
263+
};
248264

265+
try {
266+
const _client = await Promise.race([
267+
mongoClient.connect(),
268+
waitForTunnelError
269+
]);
249270
client = _client;
250-
251-
if (tunnel) {
252-
client.on('close', () => {
253-
debug('data-service disconnected. shutting down ssh tunnel');
254-
tunnel.close();
255-
});
256-
}
257-
258-
cb(null, { url: model.driverUrlWithSsh, options: validOptions });
259-
});
271+
ctx(null);
272+
return { url: model.driverUrlWithSsh, options: validOptions };
273+
} catch (err) {
274+
await closeTunnelOnError(tunnel);
275+
ctx(err);
276+
throw err;
277+
}
260278
}
261279
});
262280

@@ -332,7 +350,7 @@ const connect = (model, setupListeners, done) => {
332350

333351
logTaskStatus('Successfully connected');
334352

335-
return done(null, tasks.client, connectionOptions);
353+
return done(null, tasks.client, tasks.tunnel, connectionOptions);
336354
});
337355

338356
return tasks.state;

lib/ssh-tunnel.js

Lines changed: 0 additions & 219 deletions
This file was deleted.

package-lock.json

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@
1616
"scripts": {
1717
"ci": "npm run check && npm test",
1818
"pretest": "mongodb-runner install",
19-
"test": "mocha",
19+
"test": "mocha --timeout 15000",
2020
"check": "mongodb-js-precommit"
2121
},
2222
"peerDependencies": {
2323
"mongodb": "3.x"
2424
},
2525
"dependencies": {
26+
"@mongodb-js/ssh-tunnel": "^1.2.0",
2627
"ampersand-model": "^8.0.0",
2728
"ampersand-rest-collection": "^6.0.0",
2829
"async": "^3.1.0",

0 commit comments

Comments
 (0)