From a72a2f3fbbd170ebe97c890fb2b8bfa997648776 Mon Sep 17 00:00:00 2001 From: dimitriylol Date: Fri, 7 Dec 2018 12:38:04 +0200 Subject: [PATCH] feat: add `binary.skipMD5` and `MONGOMS_SKIP_MD5_CHECK` environment options * fix: add error handling in postinstall * feat: add MONGOMS_SKIP_MD5_CHECK for skipping md5 check * refactor: add return type in checkMd5 signature Response to the comment https://github.com/nodkz/mongodb-memory-server/pull/114#discussion_r239438607 * refactor: add checkMD5 parameter to the MongoBinaryDownload constructor For details see https://github.com/nodkz/mongodb-memory-server/pull/114#discussion_r * refactor: style changes and function renaming For details see https://github.com/nodkz/mongodb-memory-server/pull/114#discussion_r239584854 and nodkz comment after approving pull request --- README.md | 3 + postinstall.js | 5 ++ src/util/MongoBinaryDownload.js | 28 +++++++-- .../__tests__/MongoBinaryDownload-test.js | 63 +++++++++++++++++++ 4 files changed, 95 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ebb836a14..0d48fd092 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ const mongod = new MongoMemoryServer({ platform?: string, // by default os.platform() arch?: string, // by default os.arch() debug?: boolean, // by default false + skipMD5?: boolean, // by default false OR process.env.MONGOMS_SKIP_MD5_CHECK }, debug?: boolean, // by default false autoStart?: boolean, // by default true @@ -76,6 +77,8 @@ MONGOMS_VERSION=3 MONGOMS_DEBUG=1 # also available case-insensitive values: "on" "yes" "true" MONGOMS_DOWNLOAD_MIRROR=url # your mirror url to download the mongodb binary MONGOMS_DISABLE_POSTINSTALL=1 # if you want to skip download binaries on `npm i` command +MONGOMS_SKIP_MD5_CHECK=1 # if you want to skip MD5 check of downloaded binary. +# Passed constructor parameter `binary.skipMD5` has higher priority. ``` ### Replica Set start: diff --git a/postinstall.js b/postinstall.js index 96dbe777a..8c905c308 100644 --- a/postinstall.js +++ b/postinstall.js @@ -27,6 +27,11 @@ if (isModuleExists('./lib/util/MongoBinary')) { console.log('mongodb-memory-server: checking MongoDB binaries cache...'); MongoBinary.getPath({}).then(binPath => { console.log(`mongodb-memory-server: binary path is ${binPath}`); + }) + .catch((err) => { + console.log(`failed to download/install MongoDB binaries. The error: +${err}`) + process.exit(1) }); } else { console.log("Can't resolve MongoBinary module"); diff --git a/src/util/MongoBinaryDownload.js b/src/util/MongoBinaryDownload.js index 050debfa6..9955859d0 100644 --- a/src/util/MongoBinaryDownload.js +++ b/src/util/MongoBinaryDownload.js @@ -18,22 +18,38 @@ export type MongoBinaryDownloadOpts = { platform: string, arch: string, debug?: DebugPropT, + skipMD5?: boolean, }; export default class MongoBinaryDownload { debug: DebugFn; dlProgress: DownloadProgressT; + skipMD5: boolean; downloadDir: string; arch: string; version: string; platform: string; - constructor({ platform, arch, downloadDir, version, debug }: $Shape) { + constructor({ + platform, + arch, + downloadDir, + version, + skipMD5, + debug, + }: $Shape) { this.platform = platform || os.platform(); this.arch = arch || os.arch(); this.version = version || 'latest'; this.downloadDir = path.resolve(downloadDir || 'mongodb-download'); + if (skipMD5 === undefined) { + this.skipMD5 = + typeof process.env.MONGOMS_SKIP_MD5_CHECK === 'string' && + ['1', 'on', 'yes', 'true'].indexOf(process.env.MONGOMS_SKIP_MD5_CHECK.toLowerCase()) !== -1; + } else { + this.skipMD5 = skipMD5; + } this.dlProgress = { current: 0, length: 0, @@ -84,13 +100,16 @@ export default class MongoBinaryDownload { const downloadUrl = await mbdUrl.getDownloadUrl(); const mongoDBArchive = await this.download(downloadUrl); - const mongoDBArchiveMd5 = await this.download(`${downloadUrl}.md5`); - await this.checkMd5(mongoDBArchiveMd5, mongoDBArchive); + await this.checkMD5(`${downloadUrl}.md5`, mongoDBArchive); return mongoDBArchive; } - async checkMd5(mongoDBArchiveMd5: string, mongoDBArchive: string) { + async checkMD5(urlForReferenceMD5: string, mongoDBArchive: string): Promise { + if (this.skipMD5) { + return undefined; + } + const mongoDBArchiveMd5 = await this.download(urlForReferenceMD5); const signatureContent = fs.readFileSync(mongoDBArchiveMd5).toString('UTF-8'); const m = signatureContent.match(/(.*?)\s/); const md5Remote = m ? m[1] : null; @@ -98,6 +117,7 @@ export default class MongoBinaryDownload { if (md5Remote !== md5Local) { throw new Error('MongoBinaryDownload: md5 check is failed'); } + return true; } async download(downloadUrl: string) { diff --git a/src/util/__tests__/MongoBinaryDownload-test.js b/src/util/__tests__/MongoBinaryDownload-test.js index 091a9d7d0..479768d0d 100644 --- a/src/util/__tests__/MongoBinaryDownload-test.js +++ b/src/util/__tests__/MongoBinaryDownload-test.js @@ -1,8 +1,29 @@ /* @flow */ +import fs from 'fs'; +import md5file from 'md5-file'; import MongoBinaryDownload from '../MongoBinaryDownload'; +jest.mock('fs'); +jest.mock('md5-file'); + describe('MongoBinaryDownload', () => { + afterEach(() => { + delete process.env.MONGOMS_SKIP_MD5_CHECK; + }); + + it('skipMD5 attribute can be set via constructor parameter', () => { + expect(new MongoBinaryDownload({ skipMD5: true }).skipMD5).toBe(true); + expect(new MongoBinaryDownload({ skipMD5: false }).skipMD5).toBe(false); + }); + + it(`if skipMD5 input parameter is missing, then it checks +MONGOMS_SKIP_MD5_CHECK environment variable`, () => { + expect(new MongoBinaryDownload({}).skipMD5).toBe(false); + process.env.MONGOMS_SKIP_MD5_CHECK = '1'; + expect(new MongoBinaryDownload({}).skipMD5).toBe(true); + }); + it('should use direct download', async () => { process.env['yarn_https-proxy'] = ''; process.env.yarn_proxy = ''; @@ -34,4 +55,46 @@ describe('MongoBinaryDownload', () => { expect(callArg1.agent).toBeDefined(); expect(callArg1.agent.options.href).toBe('http://user:pass@proxy:8080/'); }); + + it(`checkMD5 returns true if md5 of downloaded mongoDBArchive is +the same as in the reference result`, () => { + const someMd5 = 'md5'; + fs.readFileSync.mockImplementationOnce(() => `${someMd5} fileName`); + md5file.sync.mockImplementationOnce(() => someMd5); + const mongoDBArchivePath = '/some/path'; + const fileWithReferenceMd5 = '/another/path'; + const du = new MongoBinaryDownload({}); + // $FlowFixMe + du.download = jest.fn(() => Promise.resolve(fileWithReferenceMd5)); + const urlToMongoDBArchivePath = 'some-url'; + return du.checkMD5(urlToMongoDBArchivePath, mongoDBArchivePath).then(res => { + expect(res).toBe(true); + expect(du.download).toBeCalledWith(urlToMongoDBArchivePath); + expect(fs.readFileSync).toBeCalledWith(fileWithReferenceMd5); + expect(md5file.sync).toBeCalledWith(mongoDBArchivePath); + }); + }); + + it(`checkMD5 throws an error if md5 of downloaded mongoDBArchive is NOT + the same as in the reference result`, () => { + fs.readFileSync.mockImplementationOnce(() => 'someMd5 fileName'); + md5file.sync.mockImplementationOnce(() => 'anotherMd5'); + const du = new MongoBinaryDownload({}); + // $FlowFixMe + du.download = jest.fn(() => Promise.resolve('')); + expect(du.checkMD5('', '')).rejects.toMatchInlineSnapshot( + `[Error: MongoBinaryDownload: md5 check is failed]` + ); + }); + + it('true value of skipMD5 attribute disables checkMD5 validation', () => { + expect.assertions(1); + fs.readFileSync.mockImplementationOnce(() => 'someMd5 fileName'); + md5file.sync.mockImplementationOnce(() => 'anotherMd5'); + const du = new MongoBinaryDownload({}); + du.skipMD5 = true; + return du.checkMD5('', '').then(res => { + expect(res).toBe(undefined); + }); + }); });