diff --git a/lib/instance-detail-helper.js b/lib/instance-detail-helper.js index f8267b85..a5eec569 100644 --- a/lib/instance-detail-helper.js +++ b/lib/instance-detail-helper.js @@ -61,7 +61,8 @@ function parseBuildInfo(resp) { debug: resp.debug, for_bits: resp.bits, max_bson_object_size: resp.maxBsonObjectSize, - enterprise_module: false + enterprise_module: false, + raw: resp // Save the raw output to later determine if genuine MongoDB }; // cover both cases of detecting enterprise module, see SERVER-18099 if (resp.gitVersion && resp.gitVersion.match(/enterprise/)) { @@ -94,6 +95,43 @@ function getBuildInfo(results, done) { }); } +function getCmdLineOpts(results, done) { + const db = results.db; + + const spec = { + getCmdLineOpts: 1 + }; + + const adminDb = db.databaseName === 'admin' ? db : db.admin(); + adminDb.command(spec, {}, function(err, res) { + if (err) { + debug('getCmdLineOpts failed!', err); + return done(null, { errmsg: err.message }); + } + done(null, res); + }); +} + +function getGenuineMongoDB(results, done) { + const buildInfo = results.build.raw; + const cmdLineOpts = results.cmdLineOpts; + + const res = { + isGenuine: true, + dbType: 'mongodb' + }; + + if (buildInfo.hasOwnProperty('_t' )) { + res.isGenuine = false; + res.dbType = 'cosmosdb'; + } + if (cmdLineOpts.hasOwnProperty('errmsg') && cmdLineOpts.errmsg.indexOf('not supported') !== -1) { + res.isGenuine = false; + res.dbType = 'documentdb'; + } + done(null, res); +} + /** * @param {Object} resp - Result of `db.db('admin').command({hostInfo: 1})`. * @return {Object} @@ -487,6 +525,8 @@ function getInstanceDetail(client, db, done) { host: ['client', 'db', getHostInfo], build: ['client', 'db', getBuildInfo], + cmdLineOpts: ['client', 'db', getCmdLineOpts], + genuineMongoDB: ['build', 'cmdLineOpts', getGenuineMongoDB], listDatabases: ['client', 'db', 'userInfo', listDatabases], allowedDatabases: ['userInfo', getAllowedDatabases], @@ -508,7 +548,7 @@ function getInstanceDetail(client, db, done) { } // cleanup results = omit(results, ['db', 'listDatabases', 'allowedDatabases', - 'userInfo', 'listCollections', 'allowedCollections']); + 'userInfo', 'listCollections', 'allowedCollections', 'cmdLineOpts']); return done(null, results); }); } @@ -545,7 +585,7 @@ function getInstance(client, db, done) { } res._id = [hostname, port].join(':'); - debug('instance.get returning %j', res); + debug('instance.get returning', res); done(null, res); }); } @@ -554,7 +594,9 @@ module.exports = { getAllowedCollections, getAllowedDatabases, getBuildInfo, + getCmdLineOpts, getDatabaseCollections, + getGenuineMongoDB, getHostInfo, getInstance, listCollections, diff --git a/package-lock.json b/package-lock.json index ce0a4e7b..77214df8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -196,7 +196,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -412,7 +412,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -744,7 +744,7 @@ }, "commander": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.9.0.tgz", "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", "dev": true, "requires": { @@ -776,7 +776,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -1052,7 +1052,7 @@ }, "dependency-check": { "version": "2.10.1", - "resolved": "https://registry.npmjs.org/dependency-check/-/dependency-check-2.10.1.tgz", + "resolved": "http://registry.npmjs.org/dependency-check/-/dependency-check-2.10.1.tgz", "integrity": "sha512-gmLQXELyRvWwy0IeSOMgqRvs5lotLhMO9n5932lfXhkyZ7i7wqAQ/zBoued07qRvgvo9Byol98sG8HbYKoTpNA==", "dev": true, "requires": { @@ -3728,7 +3728,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "mkdirp": { @@ -3741,7 +3741,7 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" } } @@ -4454,19 +4454,19 @@ "dependencies": { "lodash": { "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "resolved": "http://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", "dev": true }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "mkdirp": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", "dev": true, "requires": { @@ -4475,7 +4475,7 @@ }, "rimraf": { "version": "2.2.6", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.6.tgz", + "resolved": "http://registry.npmjs.org/rimraf/-/rimraf-2.2.6.tgz", "integrity": "sha1-xZWXVpsU2VatKcrMQr3d9fDqT0w=", "dev": true } @@ -5891,7 +5891,7 @@ }, "stream-combiner": { "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", + "resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=", "optional": true, "requires": { @@ -6089,7 +6089,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", diff --git a/test/fixtures.js b/test/fixtures.js index 001641ed..c082dc58 100644 --- a/test/fixtures.js +++ b/test/fixtures.js @@ -493,11 +493,58 @@ var BUILD_INFO_3_2 = { "ok" : 1 }; +var CMD_LINE_OPTS = { + "argv" : [ + "/opt/mongodb-osx-x86_64-enterprise-3.6.3/bin/mongod", + "--dbpath=/Users/user/testdata" + ], + "parsed" : { + "storage" : { + "dbPath" : "/Users/user/testdata" + } + }, + "ok" : 1 +}; + +var DOCUMENTDB_CMD_LINE_OPTS = { + "ok" : 0, + "errmsg" : "Feature not supported: getCmdLineOpts", + "code" : 303 +}; + +var COSMOSDB_BUILD_INFO = { + "_t" : "BuildInfoResponse", + "ok" : 1, + "version" : "3.2.0", + "gitVersion" : "45d947729a0315accb6d4f15a6b06be6d9c19fe7", + "targetMinOS" : "Windows 7/Windows Server 2008 R2", + "modules" : [ ], + "allocator" : "tcmalloc", + "javascriptEngine" : "Chakra", + "sysInfo" : "deprecated", + "versionArray" : [ + 3, + 2, + 0, + 0 + ], + "bits" : 64, + "debug" : false, + "maxBsonObjectSize" : 524288, + "openssl" : { + "running" : "OpenSSL 1.0.1p-fips 9 Jul 2015", + "compiled" : "OpenSSL 1.0.1p-fips 9 Jul 2015" + } +}; + module.exports = { HOST_INFO: HOST_INFO, BUILD_INFO_OLD: BUILD_INFO_OLD, BUILD_INFO_3_2: BUILD_INFO_3_2, USER_INFO_JOHN: USER_INFO_JOHN, USER_INFO_LISTDB_ONLY: USER_INFO_LISTDB_ONLY, - USER_INFO_COLL_ONLY: USER_INFO_COLL_ONLY + USER_INFO_COLL_ONLY: USER_INFO_COLL_ONLY, + CMD_LINE_OPTS: CMD_LINE_OPTS, + DOCUMENTDB_CMD_LINE_OPTS: DOCUMENTDB_CMD_LINE_OPTS, + COSMOSDB_BUILD_INFO: COSMOSDB_BUILD_INFO }; diff --git a/test/instance-detail-helper-mocked.test.js b/test/instance-detail-helper-mocked.test.js index 0234db11..94bb3db3 100644 --- a/test/instance-detail-helper-mocked.test.js +++ b/test/instance-detail-helper-mocked.test.js @@ -3,7 +3,9 @@ const { getAllowedCollections, getAllowedDatabases, getBuildInfo, + getCmdLineOpts, getDatabaseCollections, + getGenuineMongoDB, getHostInfo, listCollections, listDatabases @@ -109,8 +111,105 @@ describe('instance-detail-helper-mocked', function() { done(); }); }); + it('should save a copy of the raw output', function(done) { + const results = { + db: makeMockDB(null, {tester: 1}) + }; + getBuildInfo(results, function(err, res) { + assert.equal(err, null); + assert.deepEqual(res.raw, {tester: 1}); + done(); + }); + }); }); + describe('getCmdLineOpts', function() { + it('should not pass on any error that getCmdLineOpts returns', function(done) { + // instead of the real db handle, pass in the mocked one + const results = { + db: makeMockDB(new Error('some strange error'), null) + }; + getCmdLineOpts(results, function(err, res) { + assert.equal(err, null); + assert.equal(res.errmsg, 'some strange error'); + done(); + }); + }); + it('should return results if they error but do not throw', function(done) { + // instead of the real db handle, pass in the mocked one + const results = { + db: makeMockDB(null, fixtures.DOCUMENTDB_CMD_LINE_OPTS) + }; + getCmdLineOpts(results, function(err, res) { + assert.equal(err, null); + assert.equal(res.errmsg, 'Feature not supported: getCmdLineOpts'); + done(); + }); + }); + it('should return results if no error', function(done) { + const results = { + db: makeMockDB(null, fixtures.CMD_LINE_OPTS) + }; + getCmdLineOpts(results, function(err, res) { + assert.equal(err, null); + assert.deepEqual(res, fixtures.CMD_LINE_OPTS); + done(); + }); + }); + }); + + describe('getGenuineMongoDB', function() { + it('reports on CosmosDB', function(done) { + const results = { + build: {raw: fixtures.COSMOSDB_BUILD_INFO}, + cmdLineOpts: fixtures.CMD_LINE_OPTS + }; + getGenuineMongoDB(results, function(err, res) { + assert.equal(err, null); + assert.equal(res.dbType, 'cosmosdb'); + assert.equal(res.isGenuine, false); + done(); + }); + }); + it('reports on DocumentDB', function(done) { + const results = { + build: {raw: fixtures.BUILD_INFO_3_2}, + cmdLineOpts: fixtures.DOCUMENTDB_CMD_LINE_OPTS + }; + getGenuineMongoDB(results, function(err, res) { + assert.equal(err, null); + assert.equal(res.dbType, 'documentdb'); + assert.equal(res.isGenuine, false); + done(); + }); + }); + it('should not report on 3.2', function(done) { + const results = { + build: {raw: fixtures.BUILD_INFO_3_2}, + cmdLineOpts: fixtures.CMD_LINE_OPTS + }; + getGenuineMongoDB(results, function(err, res) { + assert.equal(err, null); + assert.equal(res.dbType, 'mongodb'); + assert.equal(res.isGenuine, true); + done(); + }); + }); + it('should not report on older versions', function(done) { + const results = { + build: {raw: fixtures.BUILD_INFO_OLD}, + cmdLineOpts: fixtures.CMD_LINE_OPTS + }; + getGenuineMongoDB(results, function(err, res) { + assert.equal(err, null); + assert.equal(res.dbType, 'mongodb'); + assert.equal(res.isGenuine, true); + done(); + }); + }); + }); + + describe('getHostInfo', function() { it('should ignore auth errors gracefully', function(done) { // instead of the real db handle, pass in the mocked one