Skip to content

Commit 55cae09

Browse files
committed
feat(NODE-6856): end file system operations on client close
1 parent 892c14d commit 55cae09

23 files changed

+221
-116
lines changed

src/client-side-encryption/auto_encrypter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ export class AutoEncrypter {
409409
context.ns = ns;
410410
context.document = cmd;
411411

412-
const stateMachine = new StateMachine({
412+
const stateMachine = new StateMachine(this, {
413413
promoteValues: false,
414414
promoteLongs: false,
415415
proxyOptions: this._proxyOptions,
@@ -436,7 +436,7 @@ export class AutoEncrypter {
436436

437437
context.id = this._contextCounter++;
438438

439-
const stateMachine = new StateMachine({
439+
const stateMachine = new StateMachine(this, {
440440
...options,
441441
proxyOptions: this._proxyOptions,
442442
tlsOptions: this._tlsOptions,

src/client-side-encryption/client_encryption.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ export class ClientEncryption {
226226
keyMaterial
227227
});
228228

229-
const stateMachine = new StateMachine({
229+
const stateMachine = new StateMachine(this, {
230230
proxyOptions: this._proxyOptions,
231231
tlsOptions: this._tlsOptions,
232232
socketOptions: autoSelectSocketOptions(this._client.s.options)
@@ -295,7 +295,7 @@ export class ClientEncryption {
295295
}
296296
const filterBson = serialize(filter);
297297
const context = this._mongoCrypt.makeRewrapManyDataKeyContext(filterBson, keyEncryptionKeyBson);
298-
const stateMachine = new StateMachine({
298+
const stateMachine = new StateMachine(this, {
299299
proxyOptions: this._proxyOptions,
300300
tlsOptions: this._tlsOptions,
301301
socketOptions: autoSelectSocketOptions(this._client.s.options)
@@ -699,7 +699,7 @@ export class ClientEncryption {
699699
const valueBuffer = serialize({ v: value });
700700
const context = this._mongoCrypt.makeExplicitDecryptionContext(valueBuffer);
701701

702-
const stateMachine = new StateMachine({
702+
const stateMachine = new StateMachine(this, {
703703
proxyOptions: this._proxyOptions,
704704
tlsOptions: this._tlsOptions,
705705
socketOptions: autoSelectSocketOptions(this._client.s.options)
@@ -783,7 +783,7 @@ export class ClientEncryption {
783783
}
784784

785785
const valueBuffer = serialize({ v: value });
786-
const stateMachine = new StateMachine({
786+
const stateMachine = new StateMachine(this, {
787787
proxyOptions: this._proxyOptions,
788788
tlsOptions: this._tlsOptions,
789789
socketOptions: autoSelectSocketOptions(this._client.s.options)

src/client-side-encryption/state_machine.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import * as fs from 'fs/promises';
21
import { type MongoCryptContext, type MongoCryptKMSRequest } from 'mongodb-client-encryption';
32
import * as net from 'net';
43
import * as tls from 'tls';
54

5+
import { type AutoEncrypter } from '..';
66
import {
77
type BSONSerializeOptions,
88
deserialize,
@@ -25,7 +25,7 @@ import {
2525
MongoDBCollectionNamespace,
2626
promiseWithResolvers
2727
} from '../utils';
28-
import { autoSelectSocketOptions, type DataKey } from './client_encryption';
28+
import { autoSelectSocketOptions, type ClientEncryption, type DataKey } from './client_encryption';
2929
import { MongoCryptError } from './errors';
3030
import { type MongocryptdManager } from './mongocryptd_manager';
3131
import { type KMSProviders } from './providers';
@@ -186,10 +186,15 @@ export type StateMachineOptions = {
186186
*/
187187
// TODO(DRIVERS-2671): clarify CSOT behavior for FLE APIs
188188
export class StateMachine {
189+
private parent: AutoEncrypter | ClientEncryption;
190+
189191
constructor(
192+
parent: AutoEncrypter | ClientEncryption,
190193
private options: StateMachineOptions,
191194
private bsonOptions = pluckBSONSerializeOptions(options)
192-
) {}
195+
) {
196+
this.parent = parent;
197+
}
193198

194199
/**
195200
* Executes the state machine according to the specification
@@ -524,11 +529,11 @@ export class StateMachine {
524529
options: tls.ConnectionOptions
525530
): Promise<void> {
526531
if (tlsOptions.tlsCertificateKeyFile) {
527-
const cert = await fs.readFile(tlsOptions.tlsCertificateKeyFile);
532+
const cert = await this.parent._client.io.fs.readFile(tlsOptions.tlsCertificateKeyFile);
528533
options.cert = options.key = cert;
529534
}
530535
if (tlsOptions.tlsCAFile) {
531-
options.ca = await fs.readFile(tlsOptions.tlsCAFile);
536+
options.ca = await this.parent._client.io.fs.readFile(tlsOptions.tlsCAFile);
532537
}
533538
if (tlsOptions.tlsCertificateKeyFilePassword) {
534539
options.passphrase = tlsOptions.tlsCertificateKeyFilePassword;

src/cmap/auth/mongodb_oidc.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Document } from '../../bson';
22
import { MongoInvalidArgumentError, MongoMissingCredentialsError } from '../../error';
3+
import { type MongoClient } from '../../mongo_client';
34
import type { HandshakeDocument } from '../connect';
45
import type { Connection } from '../connection';
56
import { type AuthContext, AuthProvider } from './auth_provider';
@@ -115,11 +116,11 @@ export interface Workflow {
115116
}
116117

117118
/** @internal */
118-
export const OIDC_WORKFLOWS: Map<EnvironmentName, () => Workflow> = new Map();
119-
OIDC_WORKFLOWS.set('test', () => new TokenMachineWorkflow(new TokenCache()));
120-
OIDC_WORKFLOWS.set('azure', () => new AzureMachineWorkflow(new TokenCache()));
121-
OIDC_WORKFLOWS.set('gcp', () => new GCPMachineWorkflow(new TokenCache()));
122-
OIDC_WORKFLOWS.set('k8s', () => new K8SMachineWorkflow(new TokenCache()));
119+
export const OIDC_WORKFLOWS: Map<EnvironmentName, (client: MongoClient) => Workflow> = new Map();
120+
OIDC_WORKFLOWS.set('test', client => new TokenMachineWorkflow(client, new TokenCache()));
121+
OIDC_WORKFLOWS.set('azure', client => new AzureMachineWorkflow(client, new TokenCache()));
122+
OIDC_WORKFLOWS.set('gcp', client => new GCPMachineWorkflow(client, new TokenCache()));
123+
OIDC_WORKFLOWS.set('k8s', client => new K8SMachineWorkflow(client, new TokenCache()));
123124

124125
/**
125126
* OIDC auth provider.

src/cmap/auth/mongodb_oidc/azure_machine_workflow.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { MongoAzureError } from '../../../error';
33
import { get } from '../../../utils';
44
import type { MongoCredentials } from '../mongo_credentials';
55
import { type AccessToken, MachineWorkflow } from './machine_workflow';
6-
import { type TokenCache } from './token_cache';
76

87
/** Azure request headers. */
98
const AZURE_HEADERS = Object.freeze({ Metadata: 'true', Accept: 'application/json' });
@@ -22,13 +21,6 @@ const TOKEN_RESOURCE_MISSING_ERROR =
2221
* @internal
2322
*/
2423
export class AzureMachineWorkflow extends MachineWorkflow {
25-
/**
26-
* Instantiate the machine workflow.
27-
*/
28-
constructor(cache: TokenCache) {
29-
super(cache);
30-
}
31-
3224
/**
3325
* Get the token from the environment.
3426
*/

src/cmap/auth/mongodb_oidc/gcp_machine_workflow.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { MongoGCPError } from '../../../error';
22
import { get } from '../../../utils';
33
import { type MongoCredentials } from '../mongo_credentials';
44
import { type AccessToken, MachineWorkflow } from './machine_workflow';
5-
import { type TokenCache } from './token_cache';
65

76
/** GCP base URL. */
87
const GCP_BASE_URL =
@@ -16,13 +15,6 @@ const TOKEN_RESOURCE_MISSING_ERROR =
1615
'TOKEN_RESOURCE must be set in the auth mechanism properties when ENVIRONMENT is gcp.';
1716

1817
export class GCPMachineWorkflow extends MachineWorkflow {
19-
/**
20-
* Instantiate the machine workflow.
21-
*/
22-
constructor(cache: TokenCache) {
23-
super(cache);
24-
}
25-
2618
/**
2719
* Get the token from the environment.
2820
*/
Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { readFile } from 'fs/promises';
2-
1+
import { type MongoClient } from '../../../mongo_client';
2+
import { type MongoCredentials } from '../mongo_credentials';
33
import { type AccessToken, MachineWorkflow } from './machine_workflow';
4-
import { type TokenCache } from './token_cache';
54

65
/** The fallback file name */
76
const FALLBACK_FILENAME = '/var/run/secrets/kubernetes.io/serviceaccount/token';
@@ -13,17 +12,10 @@ const AZURE_FILENAME = 'AZURE_FEDERATED_TOKEN_FILE';
1312
const AWS_FILENAME = 'AWS_WEB_IDENTITY_TOKEN_FILE';
1413

1514
export class K8SMachineWorkflow extends MachineWorkflow {
16-
/**
17-
* Instantiate the machine workflow.
18-
*/
19-
constructor(cache: TokenCache) {
20-
super(cache);
21-
}
22-
2315
/**
2416
* Get the token from the environment.
2517
*/
26-
async getToken(): Promise<AccessToken> {
18+
async getToken(_credentials: MongoCredentials, client: MongoClient): Promise<AccessToken> {
2719
let filename: string;
2820
if (process.env[AZURE_FILENAME]) {
2921
filename = process.env[AZURE_FILENAME];
@@ -32,7 +24,7 @@ export class K8SMachineWorkflow extends MachineWorkflow {
3224
} else {
3325
filename = FALLBACK_FILENAME;
3426
}
35-
const token = await readFile(filename, 'utf8');
27+
const token = await client.io.fs.readFile(filename, { encoding: 'utf8' });
3628
return { access_token: token };
3729
}
3830
}

src/cmap/auth/mongodb_oidc/machine_workflow.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { setTimeout } from 'timers/promises';
22

33
import { type Document } from '../../../bson';
4+
import { type MongoClient } from '../../../mongo_client';
45
import { ns } from '../../../utils';
56
import type { Connection } from '../../connection';
67
import type { MongoCredentials } from '../mongo_credentials';
@@ -21,7 +22,10 @@ export interface AccessToken {
2122
}
2223

2324
/** @internal */
24-
export type OIDCTokenFunction = (credentials: MongoCredentials) => Promise<AccessToken>;
25+
export type OIDCTokenFunction = (
26+
credentials: MongoCredentials,
27+
client: MongoClient
28+
) => Promise<AccessToken>;
2529

2630
/**
2731
* Common behaviour for OIDC machine workflows.
@@ -31,11 +35,13 @@ export abstract class MachineWorkflow implements Workflow {
3135
cache: TokenCache;
3236
callback: OIDCTokenFunction;
3337
lastExecutionTime: number;
38+
client: MongoClient;
3439

3540
/**
3641
* Instantiate the machine workflow.
3742
*/
38-
constructor(cache: TokenCache) {
43+
constructor(client: MongoClient, cache: TokenCache) {
44+
this.client = client;
3945
this.cache = cache;
4046
this.callback = this.withLock(this.getToken.bind(this));
4147
this.lastExecutionTime = Date.now() - THROTTLE_MS;
@@ -101,7 +107,7 @@ export abstract class MachineWorkflow implements Workflow {
101107
}
102108
return token;
103109
} else {
104-
const token = await this.callback(credentials);
110+
const token = await this.callback(credentials, connection.client);
105111
this.cache.put({ accessToken: token.access_token, expiresInSeconds: token.expires_in });
106112
// Put the access token on the connection as well.
107113
connection.accessToken = token.access_token;
@@ -129,7 +135,7 @@ export abstract class MachineWorkflow implements Workflow {
129135
await setTimeout(THROTTLE_MS - difference);
130136
}
131137
this.lastExecutionTime = Date.now();
132-
return await callback(credentials);
138+
return await callback(credentials, this.client);
133139
});
134140
return await lock;
135141
};
@@ -138,5 +144,5 @@ export abstract class MachineWorkflow implements Workflow {
138144
/**
139145
* Get the token from the environment or endpoint.
140146
*/
141-
abstract getToken(credentials: MongoCredentials): Promise<AccessToken>;
147+
abstract getToken(credentials: MongoCredentials, client: MongoClient): Promise<AccessToken>;
142148
}
Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import * as fs from 'fs';
2-
31
import { MongoAWSError } from '../../../error';
2+
import { type MongoClient } from '../../../mongo_client';
3+
import { type MongoCredentials } from '../mongo_credentials';
44
import { type AccessToken, MachineWorkflow } from './machine_workflow';
5-
import { type TokenCache } from './token_cache';
65

76
/** Error for when the token is missing in the environment. */
87
const TOKEN_MISSING_ERROR = 'OIDC_TOKEN_FILE must be set in the environment.';
@@ -13,22 +12,15 @@ const TOKEN_MISSING_ERROR = 'OIDC_TOKEN_FILE must be set in the environment.';
1312
* @internal
1413
*/
1514
export class TokenMachineWorkflow extends MachineWorkflow {
16-
/**
17-
* Instantiate the machine workflow.
18-
*/
19-
constructor(cache: TokenCache) {
20-
super(cache);
21-
}
22-
2315
/**
2416
* Get the token from the environment.
2517
*/
26-
async getToken(): Promise<AccessToken> {
18+
async getToken(_: MongoCredentials, client: MongoClient): Promise<AccessToken> {
2719
const tokenFile = process.env.OIDC_TOKEN_FILE;
2820
if (!tokenFile) {
2921
throw new MongoAWSError(TOKEN_MISSING_ERROR);
3022
}
31-
const token = await fs.promises.readFile(tokenFile, 'utf8');
23+
const token = await client.io.fs.readFile(tokenFile, { encoding: 'utf8' });
3224
return { access_token: token };
3325
}
3426
}

src/cmap/connect.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
MongoRuntimeError,
1717
needsRetryableWriteLabel
1818
} from '../error';
19+
import { type Monitor, type RTTPinger } from '../sdam/monitor';
1920
import { HostAddress, ns, promiseWithResolvers } from '../utils';
2021
import { AuthContext } from './auth/auth_provider';
2122
import { AuthMechanism } from './auth/providers';
@@ -25,6 +26,7 @@ import {
2526
type ConnectionOptions,
2627
CryptoConnection
2728
} from './connection';
29+
import { type ConnectionPool } from './connection_pool';
2830
import {
2931
MAX_SUPPORTED_SERVER_VERSION,
3032
MAX_SUPPORTED_WIRE_VERSION,
@@ -35,11 +37,14 @@ import {
3537
/** @public */
3638
export type Stream = Socket | TLSSocket;
3739

38-
export async function connect(options: ConnectionOptions): Promise<Connection> {
40+
export async function connect(
41+
parent: Monitor | RTTPinger | ConnectionPool,
42+
options: ConnectionOptions
43+
): Promise<Connection> {
3944
let connection: Connection | null = null;
4045
try {
4146
const socket = await makeSocket(options);
42-
connection = makeConnection(options, socket);
47+
connection = makeConnection(parent, options, socket);
4348
await performInitialHandshake(connection, options);
4449
return connection;
4550
} catch (error) {
@@ -48,13 +53,17 @@ export async function connect(options: ConnectionOptions): Promise<Connection> {
4853
}
4954
}
5055

51-
export function makeConnection(options: ConnectionOptions, socket: Stream): Connection {
56+
export function makeConnection(
57+
parent: Monitor | RTTPinger | ConnectionPool,
58+
options: ConnectionOptions,
59+
socket: Stream
60+
): Connection {
5261
let ConnectionType = options.connectionType ?? Connection;
5362
if (options.autoEncrypter) {
5463
ConnectionType = CryptoConnection;
5564
}
5665

57-
return new ConnectionType(socket, options);
66+
return new ConnectionType(parent, socket, options);
5867
}
5968

6069
function checkSupportedServer(hello: Document, options: ConnectionOptions) {

0 commit comments

Comments
 (0)