From f49a8a13c390c1623d7d95b5b900137cda36ebaa Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Tue, 26 Nov 2024 12:58:54 +0000 Subject: [PATCH 1/2] disable utf8 validation when retrieving metadata compass needs in order to function --- packages/data-service/src/data-service.ts | 77 ++++++++++++----------- packages/data-service/src/run-command.ts | 2 +- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/packages/data-service/src/data-service.ts b/packages/data-service/src/data-service.ts index 2761fd7ba59..6037de80633 100644 --- a/packages/data-service/src/data-service.ts +++ b/packages/data-service/src/data-service.ts @@ -1083,49 +1083,52 @@ class DataServiceImpl extends WithLogContext implements DataService { try { const coll = this._collection(ns, 'CRUD'); const collStats = await coll - .aggregate([ - { $collStats: { storageStats: {} } }, - { - $group: { - _id: null, - capped: { $first: '$storageStats.capped' }, - count: { $sum: '$storageStats.count' }, - size: { $sum: { $toDouble: '$storageStats.size' } }, - storageSize: { - $sum: { $toDouble: '$storageStats.storageSize' }, - }, - totalIndexSize: { - $sum: { $toDouble: '$storageStats.totalIndexSize' }, - }, - freeStorageSize: { - $sum: { $toDouble: '$storageStats.freeStorageSize' }, - }, - unscaledCollSize: { - $sum: { - $multiply: [ - { $toDouble: '$storageStats.avgObjSize' }, - { $toDouble: '$storageStats.count' }, - ], + .aggregate( + [ + { $collStats: { storageStats: {} } }, + { + $group: { + _id: null, + capped: { $first: '$storageStats.capped' }, + count: { $sum: '$storageStats.count' }, + size: { $sum: { $toDouble: '$storageStats.size' } }, + storageSize: { + $sum: { $toDouble: '$storageStats.storageSize' }, + }, + totalIndexSize: { + $sum: { $toDouble: '$storageStats.totalIndexSize' }, }, + freeStorageSize: { + $sum: { $toDouble: '$storageStats.freeStorageSize' }, + }, + unscaledCollSize: { + $sum: { + $multiply: [ + { $toDouble: '$storageStats.avgObjSize' }, + { $toDouble: '$storageStats.count' }, + ], + }, + }, + nindexes: { $max: '$storageStats.nindexes' }, }, - nindexes: { $max: '$storageStats.nindexes' }, }, - }, - { - $addFields: { - // `avgObjSize` is the average of per-shard `avgObjSize` weighted by `count` - avgObjSize: { - $cond: { - if: { $ne: ['$count', 0] }, - then: { - $divide: ['$unscaledCollSize', { $toDouble: '$count' }], + { + $addFields: { + // `avgObjSize` is the average of per-shard `avgObjSize` weighted by `count` + avgObjSize: { + $cond: { + if: { $ne: ['$count', 0] }, + then: { + $divide: ['$unscaledCollSize', { $toDouble: '$count' }], + }, + else: 0, }, - else: 0, }, }, }, - }, - ]) + ], + { enableUtf8Validation: false } + ) .toArray(); if (!collStats || collStats[0] === undefined) { @@ -1226,7 +1229,7 @@ class DataServiceImpl extends WithLogContext implements DataService { try { const cursor = this._database(databaseName, 'CRUD').listCollections( filter, - { nameOnly } + { nameOnly, enableUtf8Validation: false } ); // Iterate instead of using .toArray() so we can emit // collection info update events as they come in. diff --git a/packages/data-service/src/run-command.ts b/packages/data-service/src/run-command.ts index b31c22f2811..894bc9500d7 100644 --- a/packages/data-service/src/run-command.ts +++ b/packages/data-service/src/run-command.ts @@ -334,7 +334,7 @@ export const runCommand: RunCommand = ( return db.command( { ...spec }, - { readPreference, ...options } + { readPreference, enableUtf8Validation: false, ...options } // It's pretty hard to convince TypeScript that we are doing the right thing // here due to how vague the driver types are hence the `any` assertion // eslint-disable-next-line @typescript-eslint/no-explicit-any From d44a76afb7bb6a035923d1013a2d4e50a17400d7 Mon Sep 17 00:00:00 2001 From: Le Roux Bodenstein Date: Tue, 26 Nov 2024 17:05:13 +0000 Subject: [PATCH 2/2] { enableUtf8Validation: false } on each runCommand rather so we can argue about them one by one --- packages/data-service/src/data-service.ts | 54 +++++++++++++------ .../src/instance-detail-helper.ts | 30 +++++++---- packages/data-service/src/run-command.ts | 2 +- 3 files changed, 61 insertions(+), 25 deletions(-) diff --git a/packages/data-service/src/data-service.ts b/packages/data-service/src/data-service.ts index 6037de80633..761f60809f4 100644 --- a/packages/data-service/src/data-service.ts +++ b/packages/data-service/src/data-service.ts @@ -1168,7 +1168,11 @@ class DataServiceImpl extends WithLogContext implements DataService { @op(mongoLogId(1_001_000_031)) async killOp(id: number, comment?: string): Promise { const db = this._database('admin', 'META'); - return runCommand(db, { killOp: 1, id, comment }); + return runCommand( + db, + { killOp: 1, id, comment }, + { enableUtf8Validation: false } + ); } isWritable(): boolean { @@ -1186,10 +1190,14 @@ class DataServiceImpl extends WithLogContext implements DataService { @op(mongoLogId(1_001_000_100)) private async _connectionStatus(): Promise { const adminDb = this._database('admin', 'META'); - return await runCommand(adminDb, { - connectionStatus: 1, - showPrivileges: true, - }); + return await runCommand( + adminDb, + { + connectionStatus: 1, + showPrivileges: true, + }, + { enableUtf8Validation: false } + ); } private async _getPrivilegesOrFallback( @@ -1229,7 +1237,7 @@ class DataServiceImpl extends WithLogContext implements DataService { try { const cursor = this._database(databaseName, 'CRUD').listCollections( filter, - { nameOnly, enableUtf8Validation: false } + { nameOnly } ); // Iterate instead of using .toArray() so we can emit // collection info update events as they come in. @@ -1344,12 +1352,16 @@ class DataServiceImpl extends WithLogContext implements DataService { const listDatabases = async () => { try { - const { databases } = await runCommand(adminDb, { - listDatabases: 1, - nameOnly, - } as { - listDatabases: 1; - }); + const { databases } = await runCommand( + adminDb, + { + listDatabases: 1, + nameOnly, + } as { + listDatabases: 1; + }, + { enableUtf8Validation: false } + ); return databases; } catch (err) { // Currently Compass should not fail if listDatabase failed for any @@ -2115,7 +2127,11 @@ class DataServiceImpl extends WithLogContext implements DataService { }) async serverStatus(): Promise { const admin = this._database('admin', 'META'); - return await runCommand(admin, { serverStatus: 1 }); + return await runCommand( + admin, + { serverStatus: 1 }, + { enableUtf8Validation: false } + ); } @op(mongoLogId(1_001_000_062), (_, result) => { @@ -2123,7 +2139,11 @@ class DataServiceImpl extends WithLogContext implements DataService { }) async top(): Promise<{ totals: Record }> { const adminDb = this._database('admin', 'META'); - return await runCommand(adminDb, { top: 1 }); + return await runCommand( + adminDb, + { top: 1 }, + { enableUtf8Validation: false } + ); } @op( @@ -2460,7 +2480,11 @@ class DataServiceImpl extends WithLogContext implements DataService { name: string ): Promise & { name: string }> { const db = this._database(name, 'META'); - const stats = await runCommand(db, { dbStats: 1 }); + const stats = await runCommand( + db, + { dbStats: 1 }, + { enableUtf8Validation: false } + ); const normalized = adaptDatabaseInfo(stats); return { name, ...normalized }; } diff --git a/packages/data-service/src/instance-detail-helper.ts b/packages/data-service/src/instance-detail-helper.ts index 02b733113b2..e9d04502324 100644 --- a/packages/data-service/src/instance-detail-helper.ts +++ b/packages/data-service/src/instance-detail-helper.ts @@ -121,22 +121,34 @@ export async function getInstance( atlasVersionResult, isLocalAtlas, ] = await Promise.all([ - runCommand(adminDb, { connectionStatus: 1, showPrivileges: true }).catch( - ignoreNotAuthorized(null) + runCommand( + adminDb, + { connectionStatus: 1, showPrivileges: true }, + { enableUtf8Validation: false } + ).catch(ignoreNotAuthorized(null)), + runCommand(adminDb, { hostInfo: 1 }, { enableUtf8Validation: false }).catch( + ignoreNotAuthorized({}) ), - runCommand(adminDb, { hostInfo: 1 }).catch(ignoreNotAuthorized({})), // This command should always pass, if it throws, somethings is really off. // This is why it's the only one where we are not ignoring any types of // errors - runCommand(adminDb, { buildInfo: 1 }), + runCommand(adminDb, { buildInfo: 1 }, { enableUtf8Validation: false }), // This command is only here to get data for the logs and telemetry, if it // failed (e.g., not authorised or not supported) we should just ignore the // failure - runCommand<{ featureCompatibilityVersion: { version: string } }>(adminDb, { - getParameter: 1, - featureCompatibilityVersion: 1, - }).catch(() => null), - runCommand(adminDb, { atlasVersion: 1 }).catch(() => { + runCommand<{ featureCompatibilityVersion: { version: string } }>( + adminDb, + { + getParameter: 1, + featureCompatibilityVersion: 1, + }, + { enableUtf8Validation: false } + ).catch(() => null), + runCommand( + adminDb, + { atlasVersion: 1 }, + { enableUtf8Validation: false } + ).catch(() => { return { atlasVersion: '', gitVersion: '' }; }), checkIsLocalAtlas( diff --git a/packages/data-service/src/run-command.ts b/packages/data-service/src/run-command.ts index 894bc9500d7..b31c22f2811 100644 --- a/packages/data-service/src/run-command.ts +++ b/packages/data-service/src/run-command.ts @@ -334,7 +334,7 @@ export const runCommand: RunCommand = ( return db.command( { ...spec }, - { readPreference, enableUtf8Validation: false, ...options } + { readPreference, ...options } // It's pretty hard to convince TypeScript that we are doing the right thing // here due to how vague the driver types are hence the `any` assertion // eslint-disable-next-line @typescript-eslint/no-explicit-any