Skip to content

Commit c0f2529

Browse files
authored
feat: include csfle shared library COMPASS-5618 (#3052)
1 parent 2bc9054 commit c0f2529

File tree

12 files changed

+228
-8
lines changed

12 files changed

+228
-8
lines changed

configs/webpack-config-compass/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
lessLoader,
2020
assetsLoader,
2121
resourceLoader,
22+
sharedObjectLoader,
2223
} from './loaders';
2324
import {
2425
entriesToNamedEntries,
@@ -58,6 +59,7 @@ export function createElectronMainConfig(
5859
javascriptLoader(opts),
5960
nodeLoader(opts),
6061
resourceLoader(opts),
62+
sharedObjectLoader(opts),
6163
sourceLoader(opts),
6264
],
6365
},
@@ -124,6 +126,7 @@ export function createElectronRendererConfig(
124126
cssLoader(opts),
125127
lessLoader(opts),
126128
assetsLoader(opts),
129+
sharedObjectLoader(opts),
127130
sourceLoader(opts),
128131
],
129132
},

configs/webpack-config-compass/src/loaders.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,23 @@ export const resourceLoader = (_args: ConfigArgs) => ({
162162
type: 'asset/resource',
163163
});
164164

165+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
166+
export const sharedObjectLoader = (_args: ConfigArgs) => ({
167+
test: /\.(dylib|so|dll)(\?.+?)?$/,
168+
// asset/resource always compiles imports to paths to files, this is a good
169+
// strategy for electron main (node.js) process where handling data uris might
170+
// be more work than handling files
171+
type: 'asset/resource',
172+
});
173+
165174
export const sourceLoader = (args: ConfigArgs) => ({
166175
exclude: [
167176
javascriptLoader(args).test,
168177
nodeLoader(args).test,
169178
cssLoader(args).test,
170179
lessLoader(args).test,
171180
assetsLoader(args).test,
181+
sharedObjectLoader(args).test,
172182
// Produced by html-webpack-plugin and should not be handled
173183
/\.(ejs|html)$/,
174184
// Handled nicely by Webpack by default, no need to load it as raw source

packages/compass-e2e-tests/tests/logging.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,17 @@ describe('Logging and Telemetry integration', function () {
135135
expect(actual.arch).to.equal(process.arch);
136136
},
137137
},
138+
{
139+
s: 'I',
140+
c: 'COMPASS-MAIN',
141+
id: 1_001_000_125,
142+
ctx: 'CSFLE',
143+
msg: 'Found CSFLE library',
144+
attr: (actual: any) => {
145+
expect(actual.csfleLibraryPath).to.be.a('string');
146+
expect(actual.csfleLibraryPath).to.include('mongo_csfle_v1');
147+
},
148+
},
138149
{
139150
s: 'I',
140151
c: 'COMPASS-TELEMETRY',

packages/compass/LICENSE

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -553,5 +553,8 @@
553553
waiver of all civil liability in connection with the Program, unless a
554554
warranty or assumption of liability accompanies a copy of the Program in
555555
return for a fee.
556-
557-
END OF TERMS AND CONDITIONS
556+
557+
END OF TERMS AND CONDITIONS
558+
559+
This product also includes the MongoDB Client-Side Field-Level Encryption library
560+
(mongodb_csfle_v1) which is covered by the MongoDB Enterprise Agreement.

packages/compass/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@
106106
"**/node-addon-api/**",
107107
"**/win-export-certificate-and-key/**",
108108
"**/macos-export-certificate-and-key/**",
109-
"**/system-ca/**"
109+
"**/system-ca/**",
110+
"**/mongo_csfle_v1.*"
110111
]
111112
},
112113
"rebuild": {
@@ -123,7 +124,7 @@
123124
}
124125
},
125126
"scripts": {
126-
"install": "node scripts/download-akzidenz.js",
127+
"install": "node scripts/download-akzidenz.js && node scripts/download-csfle.js",
127128
"electron-rebuild": "electron-rebuild --only kerberos,keytar,interruptor,os-dns-native,win-export-certificate-and-key,macos-export-certificate-and-key --force --prebuild-tag-prefix not-real-prefix-to-force-rebuild",
128129
"prestart": "npm run electron-rebuild",
129130
"start": "npm run webpack serve -- --mode development",
@@ -221,6 +222,7 @@
221222
"darkreader": "^4.9.40",
222223
"debug": "4.3.0",
223224
"debug-menu": "^0.3.0",
225+
"decompress": "^4.2.1",
224226
"depcheck": "^1.4.1",
225227
"electron": "^13.5.1",
226228
"electron-devtools-installer": "^3.2.0",
@@ -246,6 +248,7 @@
246248
"mongodb": "^4.4.0",
247249
"mongodb-connection-model": "^21.16.0",
248250
"mongodb-data-service": "^21.20.0",
251+
"mongodb-download-url": "^1.2.0",
249252
"mongodb-instance-model": "^11.22.0",
250253
"mongodb-log-writer": "^1.1.4",
251254
"mongodb-url": "^3.0.3",
@@ -261,6 +264,7 @@
261264
"sinon": "^8.1.1",
262265
"sinon-chai": "^3.7.0",
263266
"storage-mixin": "^4.14.0",
267+
"tar": "^6.1.11",
264268
"uuid": "^3.0.0",
265269
"web-vitals": "^2.1.2"
266270
},

packages/compass/scripts/download-akzidenz.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
/* eslint-disable no-console */
88
const os = require('os');
99
const path = require('path');
10-
const { promises: fs } = require('fs');
10+
const { promises: fs, createWriteStream } = require('fs');
11+
const { pipeline } = require('stream');
12+
const { promisify } = require('util');
1113

1214
const PACKAGE_ROOT = process.cwd();
1315

@@ -76,7 +78,7 @@ const download = async(url, destDir) => {
7678
}
7779

7880
if (!UPDATE_CACHE) {
79-
await fs.writeFile(destFilePath, await res.buffer());
81+
await promisify(pipeline)(res.body, createWriteStream(destFilePath));
8082
}
8183
};
8284

@@ -105,9 +107,10 @@ const download = async(url, destDir) => {
105107
} catch (e) { /* ignore */ }
106108
} else {
107109
console.log(
108-
'Downloading %d fonts for package %s',
110+
'Downloading %d fonts for package %s to %s',
109111
AKZIDENZ_CDN_URLS.length,
110-
packageJson.name
112+
packageJson.name,
113+
FONTS_DIRECTORY
111114
);
112115
}
113116

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/* eslint-disable no-console */
2+
const os = require('os');
3+
const path = require('path');
4+
const { promises: fs, createWriteStream, createReadStream } = require('fs');
5+
const { getDownloadURL } = require('mongodb-download-url');
6+
const { pipeline } = require('stream');
7+
const { promisify } = require('util');
8+
const decompress = require('decompress');
9+
const tar = require('tar');
10+
11+
const PACKAGE_ROOT = process.cwd();
12+
13+
// const MONOREPO_ROOT = path.resolve(__dirname, '..');
14+
15+
const CACHE_DIR = path.join(os.tmpdir(), '.compass-csfle-library-cache');
16+
17+
const fetch = require('make-fetch-happen').defaults({
18+
cacheManager: CACHE_DIR
19+
});
20+
21+
const UPDATE_CACHE = process.argv.includes('--update-cache');
22+
23+
const CSFLE_DIRECTORY = path.resolve(
24+
PACKAGE_ROOT,
25+
'src',
26+
'deps',
27+
'csfle'
28+
);
29+
30+
const download = async(url, destDir) => {
31+
const destFileName = path.basename(url);
32+
const destFilePath = path.join(destDir, destFileName);
33+
34+
const res = await fetch(url);
35+
36+
// If cache item stored by `make-fetch-happen` is stale, it will check if
37+
// resource was not modified with the remote and can return a 304 with a body
38+
// present. In that case we don't want to throw an error, but rather proceed
39+
// with the normal flow
40+
if (!res.ok && res.status !== 304) {
41+
throw new Error(`Failed to fetch ${url}: ${res.statusText}`);
42+
}
43+
44+
if (!UPDATE_CACHE) {
45+
await promisify(pipeline)(res.body, createWriteStream(destFilePath));
46+
}
47+
48+
return destFilePath;
49+
};
50+
51+
(async() => {
52+
const packageJson = require(path.join(PACKAGE_ROOT, 'package.json'));
53+
54+
if (UPDATE_CACHE) {
55+
console.log('Re-populating csfle library cache at %s', CACHE_DIR);
56+
57+
try {
58+
await fs.rmdir(CACHE_DIR, { recursive: true });
59+
} catch (e) { /* ignore */ }
60+
} else {
61+
console.log(
62+
'Downloading csfle library for package %s',
63+
packageJson.name
64+
);
65+
}
66+
67+
const downloadOptions = {
68+
enterprise: true,
69+
csfle: true
70+
};
71+
if (process.platform === 'linux') {
72+
// The CSFLE shared library is built for different distros,
73+
// but since it only depends on glibc, we can just download
74+
// a CSFLE library from a distro with a low glibc version
75+
// such as RHEL7.
76+
downloadOptions.distro = 'rhel70';
77+
}
78+
let artifactInfo;
79+
// Try getting the latest stable csfle library, if none exists
80+
// (which is the case at the time of writing), fall back to
81+
// a 6.0 rc candidate. We can remove this try/catch after 6.0.0.
82+
try {
83+
artifactInfo = await getDownloadURL(downloadOptions);
84+
} catch {
85+
artifactInfo = await getDownloadURL({
86+
...downloadOptions,
87+
version: '>= 6.0.0-rc4'
88+
});
89+
}
90+
91+
console.log('Downloading csfle artifact', artifactInfo, 'to', CSFLE_DIRECTORY);
92+
await fs.mkdir(CSFLE_DIRECTORY, { recursive: true });
93+
const artifactPath = await download(artifactInfo.url, CSFLE_DIRECTORY);
94+
95+
if (artifactInfo.ext === 'zip') {
96+
// For .zip files, use `decompress`
97+
await decompress(artifactPath, CSFLE_DIRECTORY);
98+
} else {
99+
// For .tar files, `decompress` is buggy, so we use `tar` instead
100+
await promisify(pipeline)(
101+
createReadStream(artifactPath),
102+
tar.x({
103+
C: CSFLE_DIRECTORY
104+
}));
105+
}
106+
})().catch((err) => {
107+
if (err) {
108+
console.error(err);
109+
}
110+
111+
process.exit(1);
112+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# This folder only contains files downloaded via scripts/download-csfle
2+
*
3+
!.*

packages/compass/src/main/application.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { CompassLogging } from './logging';
88
import { CompassTelemetry } from './telemetry';
99
import { CompassWindowManager } from './window-manager';
1010
import { CompassMenu } from './menu';
11+
import { setupCSFLELibrary } from './setup-csfle-library';
1112

1213
const debug = createDebug('mongodb-compass:main:application');
1314

@@ -37,6 +38,7 @@ class CompassApplication {
3738
this.setupTelemetry(),
3839
]);
3940

41+
await setupCSFLELibrary();
4042
this.setupJavaScriptArguments();
4143
this.setupLifecycleListeners();
4244
this.setupApplicationMenu();

packages/compass/src/main/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import '../setup-hadron-distribution';
2+
import './setup-csfle-library';
23
import { app } from 'electron';
34
import { handleUncaughtException } from './handle-uncaught-exception';
45

0 commit comments

Comments
 (0)