diff options
Diffstat (limited to 'src/mongo/shell')
| -rw-r--r-- | src/mongo/shell/SConscript | 1 | ||||
| -rw-r--r-- | src/mongo/shell/assert.js | 11 | ||||
| -rw-r--r-- | src/mongo/shell/check_log.js | 105 | ||||
| -rw-r--r-- | src/mongo/shell/data_consistency_checker.js | 22 | ||||
| -rw-r--r-- | src/mongo/shell/db.js | 51 | ||||
| -rw-r--r-- | src/mongo/shell/encrypted_dbclient_base.cpp | 77 | ||||
| -rw-r--r-- | src/mongo/shell/encrypted_dbclient_base.h | 60 | ||||
| -rw-r--r-- | src/mongo/shell/keyvault.js | 71 | ||||
| -rw-r--r-- | src/mongo/shell/linenoise.cpp | 5 | ||||
| -rw-r--r-- | src/mongo/shell/mongo_main.cpp | 10 | ||||
| -rw-r--r-- | src/mongo/shell/query.js | 8 | ||||
| -rw-r--r-- | src/mongo/shell/replsettest.js | 53 | ||||
| -rw-r--r-- | src/mongo/shell/servers.js | 39 | ||||
| -rw-r--r-- | src/mongo/shell/shardingtest.js | 994 | ||||
| -rw-r--r-- | src/mongo/shell/shell_utils.cpp | 22 | ||||
| -rw-r--r-- | src/mongo/shell/types.js | 30 | ||||
| -rw-r--r-- | src/mongo/shell/utils.js | 59 | ||||
| -rw-r--r-- | src/mongo/shell/utils_sh.js | 141 |
18 files changed, 708 insertions, 1051 deletions
diff --git a/src/mongo/shell/SConscript b/src/mongo/shell/SConscript index 3b742237bc6..d8f54958a73 100644 --- a/src/mongo/shell/SConscript +++ b/src/mongo/shell/SConscript @@ -203,7 +203,6 @@ if get_option('ssl') == 'on': "fle_shell_options.idl", ], LIBDEPS_PRIVATE=[ - '$BUILD_DIR/mongo/bson/bson_validate', '$BUILD_DIR/mongo/client/clientdriver_minimal', '$BUILD_DIR/mongo/crypto/aead_encryption', '$BUILD_DIR/mongo/crypto/encrypted_field_config', diff --git a/src/mongo/shell/assert.js b/src/mongo/shell/assert.js index 6579443ae2e..ff168bd865d 100644 --- a/src/mongo/shell/assert.js +++ b/src/mongo/shell/assert.js @@ -110,8 +110,7 @@ assert = (function() { doassert("msg function cannot expect any parameters."); } } else if (typeof msg !== "string" && typeof msg !== "object") { - doassert("msg parameter must be a string, function or object. Found type: " + - typeof (msg)); + doassert("msg parameter must be a string, function or object."); } if (msg && assert._debug) { @@ -842,12 +841,10 @@ assert = (function() { assert.commandWorkedOrFailedWithCode = function commandWorkedOrFailedWithCode( res, errorCodeSet, msg) { - try { - // First check if the command worked. - return assert.commandWorked(res, msg); - } catch (e) { - // If the command did not work, assert it failed with one of the specified codes. + if (!res.ok) { return assert.commandFailedWithCode(res, errorCodeSet, msg); + } else { + return assert.commandWorked(res, msg); } }; diff --git a/src/mongo/shell/check_log.js b/src/mongo/shell/check_log.js index e31dd863519..fc74d871cc1 100644 --- a/src/mongo/shell/check_log.js +++ b/src/mongo/shell/check_log.js @@ -30,32 +30,6 @@ checkLog = (function() { * is found in the logs. Note: this function does not throw an exception, so the return * value should not be ignored. */ - const getLogMessage = function(conn, msg) { - const logMessages = getGlobalLog(conn); - if (logMessages === null) { - return null; - } - if (msg instanceof RegExp) { - for (let logMsg of logMessages) { - if (logMsg.search(msg) != -1) { - return logMsg; - } - } - } else { - for (let logMsg of logMessages) { - if (logMsg.includes(msg)) { - return logMsg; - } - } - } - return null; - }; - - /* - * Calls the 'getLog' function on the provided connection 'conn' to see if the provided msg - * is found in the logs. Note: this function does not throw an exception, so the return - * value should not be ignored. - */ const checkContainsOnce = function(conn, msg) { const logMessages = getGlobalLog(conn); if (logMessages === null) { @@ -122,9 +96,26 @@ checkLog = (function() { return actual === expected; }, context = null) { - const messages = getFilteredLogMessages(conn, id, attrsDict, severity, isRelaxed, context); + const logMessages = getGlobalLog(conn); + if (logMessages === null) { + return false; + } + + let count = 0; + for (let logMsg of logMessages) { + let obj; + try { + obj = JSON.parse(logMsg); + } catch (ex) { + print('checkLog.checkContainsOnce: JsonJSON.parse() failed: ' + tojson(ex) + ': ' + + logMsg); + throw ex; + } - const count = messages.length; + if (_compareLogs(obj, id, severity, context, attrsDict, isRelaxed)) { + count++; + } + } return comparator(count, expectedCount); }; @@ -165,36 +156,6 @@ checkLog = (function() { }; /* - * See checkContainsWithCountJson comment. - */ - const getFilteredLogMessages = function( - conn, id, attrsDict, severity = null, isRelaxed = false, context = null) { - const logMessages = getGlobalLog(conn); - if (logMessages === null) { - return false; - } - - let messages = []; - - for (let logMsg of logMessages) { - let obj; - try { - obj = JSON.parse(logMsg); - } catch (ex) { - print('checkLog.checkContainsOnce: JsonJSON.parse() failed: ' + tojson(ex) + ': ' + - logMsg); - throw ex; - } - - if (_compareLogs(obj, id, severity, context, attrsDict, isRelaxed)) { - messages.push(obj); - } - } - - return messages; - }; - - /* * Calls the 'getLog' function at regular intervals on the provided connection 'conn' until * the provided 'msg' is found in the logs, or it times out. Throws an exception on timeout. */ @@ -210,29 +171,6 @@ checkLog = (function() { {runHangAnalyzer: false}); }; - /* - * Calls the 'getLog' function at regular intervals on the provided connection 'conn' until - * the provided 'msg' is found in the logs and returned, or it times out. Throws an exception on - * timeout. - */ - let containsLog = function(conn, msg, timeoutMillis = 5 * 60 * 1000, retryIntervalMS = 300) { - // Don't run the hang analyzer because we don't expect contains() to always succeed. - let logMsg = null; - assert.soon( - function() { - logMsg = getLogMessage(conn, msg); - if (logMsg) { - return true; - } - return false; - }, - 'Could not find log entries containing the following message: ' + msg, - timeoutMillis, - retryIntervalMS, - {runHangAnalyzer: false}); - return logMsg; - }; - let containsJson = function(conn, id, attrsDict, timeoutMillis = 5 * 60 * 1000) { // Don't run the hang analyzer because we don't expect contains() to always succeed. assert.soon( @@ -472,21 +410,18 @@ checkLog = (function() { return { getGlobalLog: getGlobalLog, - getLogMessage: getLogMessage, checkContainsOnce: checkContainsOnce, checkContainsOnceJson: checkContainsOnceJson, checkContainsWithCountJson: checkContainsWithCountJson, checkContainsWithAtLeastCountJson: checkContainsWithAtLeastCountJson, checkContainsOnceJsonStringMatch: checkContainsOnceJsonStringMatch, contains: contains, - containsLog: containsLog, containsJson: containsJson, containsRelaxedJson: containsRelaxedJson, containsWithCount: containsWithCount, containsWithAtLeastCount: containsWithAtLeastCount, formatAsLogLine: formatAsLogLine, - formatAsJsonLogLine: formatAsJsonLogLine, - getFilteredLogMessages: getFilteredLogMessages, + formatAsJsonLogLine: formatAsJsonLogLine }; })(); })(); diff --git a/src/mongo/shell/data_consistency_checker.js b/src/mongo/shell/data_consistency_checker.js index b3303c76857..3a1621b11de 100644 --- a/src/mongo/shell/data_consistency_checker.js +++ b/src/mongo/shell/data_consistency_checker.js @@ -370,28 +370,6 @@ var {DataConsistencyChecker} = (function() { delete syncingInfo.idIndex.ns; } - // If the servers are using encryption and they specify an encryption option - // in versions <7.2 this is stored on the primary but not the secondary. - // This is not an actual failure since the data is correct on all nodes. We - // can safely ignore this element in the configString. - const encryptionRegex = /encryption=\(?[^)]*\),?/; - - if (sourceInfo.options && sourceInfo.options.storageEngine && - sourceInfo.options.storageEngine.wiredTiger && - sourceInfo.options.storageEngine.wiredTiger.configString) { - sourceInfo.options.storageEngine.wiredTiger.configString = - sourceInfo.options.storageEngine.wiredTiger.configString.replace( - encryptionRegex, ""); - } - - if (syncingInfo.options && syncingInfo.options.storageEngine && - syncingInfo.options.storageEngine.wiredTiger && - syncingInfo.options.storageEngine.wiredTiger.configString) { - syncingInfo.options.storageEngine.wiredTiger.configString = - syncingInfo.options.storageEngine.wiredTiger.configString.replace( - encryptionRegex, ""); - } - if (!bsonBinaryEqual(syncingInfo, sourceInfo)) { prettyPrint( `the two nodes have different attributes for the collection or view ${ diff --git a/src/mongo/shell/db.js b/src/mongo/shell/db.js index 2364460d62a..d3800871af1 100644 --- a/src/mongo/shell/db.js +++ b/src/mongo/shell/db.js @@ -1745,6 +1745,57 @@ DB.prototype.watch = function(pipeline, options) { return this._runAggregate({aggregate: 1, pipeline: pipeline}, aggOptions); }; +DB.prototype.getFreeMonitoringStatus = function() { + 'use strict'; + return assert.commandWorked(this.adminCommand({getFreeMonitoringStatus: 1})); +}; + +DB.prototype.enableFreeMonitoring = function() { + 'use strict'; + let reply, isPrimary; + if (this.getMongo().getApiParameters().apiVersion) { + reply = this.hello(); + isPrimary = reply.isWritablePrimary; + } else { + reply = this.isMaster(); + isPrimary = reply.ismaster; + } + + if (!isPrimary) { + print("ERROR: db.enableFreeMonitoring() may only be run on a primary"); + return; + } + + assert.commandWorked(this.adminCommand({setFreeMonitoring: 1, action: 'enable'})); + + const cmd = this.adminCommand({getFreeMonitoringStatus: 1}); + if (!cmd.ok && (cmd.code == ErrorCode.Unauthorized)) { + // Edge case: It's technically possible that a user can change free-mon state, + // but is not allowed to inspect it. + print("Successfully initiated free monitoring, but unable to determine status " + + "as you lack the 'checkFreeMonitoringStatus' privilege."); + return; + } + assert.commandWorked(cmd); + + if (cmd.state !== 'enabled') { + const url = this.adminCommand({'getParameter': 1, 'cloudFreeMonitoringEndpointURL': 1}) + .cloudFreeMonitoringEndpointURL; + + print("Unable to get immediate response from the Cloud Monitoring service. We will" + + "continue to retry in the background. Please check your firewall " + + "settings to ensure that mongod can communicate with \"" + url + "\""); + return; + } + + print(tojson(cmd)); +}; + +DB.prototype.disableFreeMonitoring = function() { + 'use strict'; + assert.commandWorked(this.adminCommand({setFreeMonitoring: 1, action: 'disable'})); +}; + // Writing `this.hasOwnProperty` would cause DB.prototype.getCollection() to be called since the // DB's getProperty() handler in C++ takes precedence when a property isn't defined on the DB // instance directly. The "hasOwnProperty" property is defined on Object.prototype, so we must diff --git a/src/mongo/shell/encrypted_dbclient_base.cpp b/src/mongo/shell/encrypted_dbclient_base.cpp index b9151685a93..137d0c482ab 100644 --- a/src/mongo/shell/encrypted_dbclient_base.cpp +++ b/src/mongo/shell/encrypted_dbclient_base.cpp @@ -199,24 +199,22 @@ void EncryptedDBClientBase::decryptPayload(ConstDataRange data, } } -EncryptedDBClientBase::RunCommandReturn EncryptedDBClientBase::processResponseFLE1( - EncryptedDBClientBase::RunCommandReturn result, const StringData databaseName) { - auto rawReply = result.returnReply->getCommandReply(); +std::pair<rpc::UniqueReply, DBClientBase*> EncryptedDBClientBase::processResponseFLE1( + rpc::UniqueReply result, const StringData databaseName) { + auto rawReply = result->getCommandReply(); return prepareReply( std::move(result), databaseName, encryptDecryptCommand(rawReply, false, databaseName)); } -EncryptedDBClientBase::RunCommandReturn EncryptedDBClientBase::processResponseFLE2( - EncryptedDBClientBase::RunCommandReturn result, const StringData databaseName) { - auto rawReply = result.returnReply->getCommandReply(); +std::pair<rpc::UniqueReply, DBClientBase*> EncryptedDBClientBase::processResponseFLE2( + rpc::UniqueReply result, const StringData databaseName) { + auto rawReply = result->getCommandReply(); return prepareReply( std::move(result), databaseName, FLEClientCrypto::decryptDocument(rawReply, this)); } -EncryptedDBClientBase::RunCommandReturn EncryptedDBClientBase::prepareReply( - EncryptedDBClientBase::RunCommandReturn result, - const StringData databaseName, - BSONObj decryptedDoc) { +std::pair<rpc::UniqueReply, DBClientBase*> EncryptedDBClientBase::prepareReply( + rpc::UniqueReply result, const StringData databaseName, BSONObj decryptedDoc) { rpc::OpMsgReplyBuilder replyBuilder; replyBuilder.setCommandReply(StatusWith<BSONObj>(decryptedDoc)); auto msg = replyBuilder.done(); @@ -224,49 +222,22 @@ EncryptedDBClientBase::RunCommandReturn EncryptedDBClientBase::prepareReply( auto host = _conn->getServerAddress(); auto reply = _conn->parseCommandReplyMessage(host, msg); - return EncryptedDBClientBase::RunCommandReturn({std::move(reply), result}); + return {std::move(reply), this}; } -EncryptedDBClientBase::RunCommandReturn EncryptedDBClientBase::doRunCommand( - EncryptedDBClientBase::RunCommandParams params) { - if (params.type == EncryptedDBClientBase::RunCommandConnectionType::rawPtr) { - return EncryptedDBClientBase::RunCommandReturn( - _conn->runCommandWithTarget(std::move(params.request))); - } - invariant(params.conn); - return EncryptedDBClientBase::RunCommandReturn( - _conn->runCommandWithTarget(std::move(params.request), params.conn)); -} - -EncryptedDBClientBase::RunCommandReturn EncryptedDBClientBase::handleEncryptionRequest( - EncryptedDBClientBase::RunCommandParams params) { - auto commandName = params.request.getCommandName().toString(); - auto databaseName = params.request.getDatabase().toString(); +std::pair<rpc::UniqueReply, DBClientBase*> EncryptedDBClientBase::runCommandWithTarget( + OpMsgRequest request) { + std::string commandName = request.getCommandName().toString(); + std::string databaseName = request.getDatabase().toString(); if (std::find(kEncryptedCommands.begin(), kEncryptedCommands.end(), StringData(commandName)) == std::end(kEncryptedCommands)) { - return doRunCommand(std::move(params)); + return _conn->runCommandWithTarget(std::move(request)); } - EncryptedDBClientBase::RunCommandReturn result(doRunCommand(std::move(params))); - return processResponseFLE1(processResponseFLE2(std::move(result), databaseName), databaseName); -} - -std::pair<rpc::UniqueReply, DBClientBase*> EncryptedDBClientBase::runCommandWithTarget( - OpMsgRequest request) { - EncryptedDBClientBase::RunCommandParams params(request); - auto result = handleEncryptionRequest(std::move(params)); - auto returnConn = stdx::get<DBClientBase*>(result.returnConn); - return {std::move(result.returnReply), returnConn}; -} - -std::pair<rpc::UniqueReply, std::shared_ptr<DBClientBase>> -EncryptedDBClientBase::runCommandWithTarget(OpMsgRequest request, - std::shared_ptr<DBClientBase> conn) { - EncryptedDBClientBase::RunCommandParams params(request, conn); - auto result = handleEncryptionRequest(std::move(params)); - auto returnConn = stdx::get<std::shared_ptr<DBClientBase>>(result.returnConn); - return {std::move(result.returnReply), returnConn}; + auto result = _conn->runCommandWithTarget(std::move(request)).first; + return processResponseFLE1(processResponseFLE2(std::move(result), databaseName).first, + databaseName); } /** @@ -715,10 +686,6 @@ std::shared_ptr<SymmetricKey> EncryptedDBClientBase::getDataKey(const UUID& uuid return key; } -DBClientBase* EncryptedDBClientBase::getRawConnection() { - return _conn.get(); -} - SecureVector<uint8_t> EncryptedDBClientBase::getKeyMaterialFromDisk(const UUID& uuid) { NamespaceString fullNameNS = getCollectionNS(); FindCommandRequest findCmd{fullNameNS}; @@ -899,16 +866,8 @@ std::unique_ptr<DBClientBase> createEncryptedDBClientBase(std::unique_ptr<DBClie return std::move(base); } -DBClientBase* getNestedConnection(DBClientBase* conn) { - auto* encryptedConn = dynamic_cast<EncryptedDBClientBase*>(conn); - if (!encryptedConn) { - return nullptr; - } - return encryptedConn->getRawConnection(); -} - MONGO_INITIALIZER(setCallbacksForEncryptedDBClientBase)(InitializerContext*) { - mongo::mozjs::setEncryptedDBClientCallbacks(createEncryptedDBClientBase, getNestedConnection); + mongo::mozjs::setEncryptedDBClientCallback(createEncryptedDBClientBase); } } // namespace diff --git a/src/mongo/shell/encrypted_dbclient_base.h b/src/mongo/shell/encrypted_dbclient_base.h index f7a7608f70a..4c0a75434fb 100644 --- a/src/mongo/shell/encrypted_dbclient_base.h +++ b/src/mongo/shell/encrypted_dbclient_base.h @@ -102,11 +102,7 @@ public: using DBClientBase::runCommandWithTarget; virtual std::pair<rpc::UniqueReply, DBClientBase*> runCommandWithTarget( - OpMsgRequest request) final; - - std::pair<rpc::UniqueReply, std::shared_ptr<DBClientBase>> runCommandWithTarget( - OpMsgRequest request, std::shared_ptr<DBClientBase>) final; - + OpMsgRequest request) override; std::string toString() const final; int getMinWireVersion() final; @@ -157,8 +153,6 @@ public: bool isMongos() const final; - DBClientBase* getRawConnection(); - #ifdef MONGO_CONFIG_SSL const SSLConfiguration* getSSLConfiguration() override; @@ -170,54 +164,16 @@ public: protected: BSONObj _decryptResponsePayload(BSONObj& reply, StringData databaseName, bool isFLE2); - enum class RunCommandConnectionType { rawPtr, sharedPtr }; - - struct RunCommandParams { - OpMsgRequest request; - std::shared_ptr<DBClientBase> conn; - RunCommandConnectionType type; - - RunCommandParams(OpMsgRequest request) - : request(std::move(request)), type(RunCommandConnectionType::rawPtr){}; - - RunCommandParams(OpMsgRequest request, std::shared_ptr<DBClientBase> base) - : request(std::move(request)), conn(base), type(RunCommandConnectionType::sharedPtr){}; - - RunCommandParams(OpMsgRequest request, RunCommandParams params) - : request(std::move(request)), type(params.type) { - if (type == RunCommandConnectionType::sharedPtr) { - conn = params.conn; - }; - }; - }; - - using RunCommandReturnConn = stdx::variant<DBClientBase*, std::shared_ptr<DBClientBase>>; - - struct RunCommandReturn { - rpc::UniqueReply returnReply; - RunCommandReturnConn returnConn; - - RunCommandReturn(std::pair<rpc::UniqueReply, DBClientBase*> pair) - : returnReply(std::move(std::get<0>(pair))), returnConn(std::get<1>(pair)) {} - - RunCommandReturn(std::pair<rpc::UniqueReply, std::shared_ptr<DBClientBase>> pair) - : returnReply(std::move(std::get<0>(pair))), returnConn(std::get<1>(pair)) {} - - RunCommandReturn(rpc::UniqueReply reply, RunCommandReturn& result) - : returnReply(std::move(reply)), returnConn(result.returnConn) {} - }; - - RunCommandReturn doRunCommand(RunCommandParams params); - - virtual RunCommandReturn handleEncryptionRequest(RunCommandParams params); + std::pair<rpc::UniqueReply, DBClientBase*> processResponseFLE1(rpc::UniqueReply result, + StringData databaseName); - RunCommandReturn processResponseFLE1(RunCommandReturn result, StringData databaseName); + std::pair<rpc::UniqueReply, DBClientBase*> processResponseFLE2(rpc::UniqueReply result, + StringData databaseName); - RunCommandReturn processResponseFLE2(RunCommandReturn result, StringData DatabaseName); + std::pair<rpc::UniqueReply, DBClientBase*> prepareReply(rpc::UniqueReply result, + StringData databaseName, + BSONObj decryptedDoc); - RunCommandReturn prepareReply(RunCommandReturn result, - StringData databaseName, - BSONObj decryptedDoc); BSONObj encryptDecryptCommand(const BSONObj& object, bool encrypt, StringData databaseName); diff --git a/src/mongo/shell/keyvault.js b/src/mongo/shell/keyvault.js index ee8f2ddf111..c913d81a5b1 100644 --- a/src/mongo/shell/keyvault.js +++ b/src/mongo/shell/keyvault.js @@ -6,37 +6,13 @@ Mongo.prototype.getKeyVault = function() { }; class KeyVault { - _runCommand(client, func, args) { - let numRetries = 3; - do { - try { - const result = func.apply(client, args); - return result; - } catch (e) { - numRetries--; - if (!isNetworkError(e) || numRetries == 0) { - jsTest.log("KeyVault: We have exceeded the number of retries. Throwing."); - throw e; - } - - const res = this.mongo.getDB('admin')._helloOrLegacyHello(); - if (!res) { - jsTest.log("KeyVault: We do not have a connection to the database. Throwing."); - throw e; - } - this.keyColl = this.mongo.getDataKeyCollection(); - } - } while (true); - } - constructor(mongo) { this.mongo = mongo; - var collection = this._runCommand(this.mongo, this.mongo.getDataKeyCollection, {}); + var collection = mongo.getDataKeyCollection(); this.keyColl = collection; - this._runCommand(this.keyColl, this.keyColl.createIndex, [ + this.keyColl.createIndex( {keyAltNames: 1}, - {unique: true, partialFilterExpression: {keyAltNames: {$exists: true}}} - ]); + {unique: true, partialFilterExpression: {keyAltNames: {$exists: true}}}); } createKey(kmsProvider, param2 = undefined, param3 = undefined) { @@ -59,8 +35,7 @@ class KeyVault { return "TypeError: customer master key must be of String type."; } - var masterKeyAndMaterial = this._runCommand( - this.mongo, this.mongo.generateDataKey, [kmsProvider, customerMasterKey]); + var masterKeyAndMaterial = this.mongo.generateDataKey(kmsProvider, customerMasterKey); var masterKey = masterKeyAndMaterial.masterKey; var current = ISODate(); @@ -91,24 +66,24 @@ class KeyVault { doc.keyAltNames = keyAltNames; } - this._runCommand(this.keyColl, this.keyColl.insert, [doc]); + this.keyColl.insert(doc); return uuid; } getKey(keyId) { - return this._runCommand(this.keyColl, this.keyColl.find, [{"_id": keyId}]); + return this.keyColl.find({"_id": keyId}); } getKeyByAltName(keyAltName) { - return this._runCommand(this.keyColl, this.keyColl.find, [{"keyAltNames": keyAltName}]); + return this.keyColl.find({"keyAltNames": keyAltName}); } deleteKey(keyId) { - return this._runCommand(this.keyColl, this.keyColl.deleteOne, [{"_id": keyId}]); + return this.keyColl.deleteOne({"_id": keyId}); } getKeys() { - return this._runCommand(this.keyColl, this.keyColl.find, []); + return this.keyColl.find(); } addKeyAlternateName(keyId, keyAltName) { @@ -117,31 +92,27 @@ class KeyVault { if (typeof keyAltName === "object") { return "TypeError: key alternate name cannot be object or array type."; } - return this._runCommand( - this.keyColl, this.keyColl.findAndModify, [{ - query: {"_id": keyId}, - update: {$push: {"keyAltNames": keyAltName}, $currentDate: {"updateDate": true}}, - }]); + return this.keyColl.findAndModify({ + query: {"_id": keyId}, + update: {$push: {"keyAltNames": keyAltName}, $currentDate: {"updateDate": true}}, + }); } removeKeyAlternateName(keyId, keyAltName) { if (typeof keyAltName === "object") { return "TypeError: key alternate name cannot be object or array type."; } - - const ret = this._runCommand( - this.keyColl, this.keyColl.findAndModify, [{ - query: {"_id": keyId}, - update: {$pull: {"keyAltNames": keyAltName}, $currentDate: {"updateDate": true}} - }]); + const ret = this.keyColl.findAndModify({ + query: {"_id": keyId}, + update: {$pull: {"keyAltNames": keyAltName}, $currentDate: {"updateDate": true}} + }); if (ret != null && ret.keyAltNames.length === 1 && ret.keyAltNames[0] === keyAltName) { // Remove the empty array to prevent duplicate key violations - return this._runCommand( - this.keyColl, this.keyColl.findAndModify, [{ - query: {"_id": keyId, "keyAltNames": undefined}, - update: {$unset: {"keyAltNames": ""}, $currentDate: {"updateDate": true}} - }]); + return this.keyColl.findAndModify({ + query: {"_id": keyId, "keyAltNames": undefined}, + update: {$unset: {"keyAltNames": ""}, $currentDate: {"updateDate": true}} + }); } return ret; } diff --git a/src/mongo/shell/linenoise.cpp b/src/mongo/shell/linenoise.cpp index d62082aa089..679578d133b 100644 --- a/src/mongo/shell/linenoise.cpp +++ b/src/mongo/shell/linenoise.cpp @@ -119,7 +119,6 @@ #include <string> #include <vector> -#include "mongo/base/data_view.h" #include "mongo/util/errno_util.h" using std::string; @@ -353,7 +352,7 @@ public: } Utf32String killedText(text, textLen); if (lastAction == actionKill && size > 0) { - int slot = mongo::ConstDataView(&indexToSlot[0]).read<uint8_t>(); + int slot = indexToSlot[0]; int currentLen = theRing[slot].length(); int resultLen = currentLen + textLen; Utf32String temp(resultLen + 1); @@ -376,7 +375,7 @@ public: size++; theRing.push_back(killedText); } else { - int slot = mongo::ConstDataView(&indexToSlot[capacity - 1]).read<uint8_t>(); + int slot = indexToSlot[capacity - 1]; theRing[slot] = killedText; memmove(&indexToSlot[1], &indexToSlot[0], capacity - 1); indexToSlot[0] = slot; diff --git a/src/mongo/shell/mongo_main.cpp b/src/mongo/shell/mongo_main.cpp index 6d3b5f37244..b6cac96d526 100644 --- a/src/mongo/shell/mongo_main.cpp +++ b/src/mongo/shell/mongo_main.cpp @@ -116,14 +116,14 @@ const std::string kDefaultMongoURL = "mongodb://"s + kDefaultMongoHost + ":"s + // level. The server is responsible for rejecting usages of new features if its // featureCompatibilityVersion is lower. MONGO_INITIALIZER_WITH_PREREQUISITES(SetFeatureCompatibilityVersionLatest, - ("EndStartupOptionStorage")) + ("EndStartupOptionSetup")) // (Generic FCV reference): This FCV reference should exist across LTS binary versions. (InitializerContext* context) { mongo::serverGlobalParams.mutableFeatureCompatibility.setVersion( multiversion::GenericFCV::kLatest); } -MONGO_INITIALIZER_WITH_PREREQUISITES(WireSpec, ("EndStartupOptionHandling"))(InitializerContext*) { +MONGO_INITIALIZER_WITH_PREREQUISITES(WireSpec, ("EndStartupOptionSetup"))(InitializerContext*) { WireSpec::instance().initialize(WireSpec::Specification{}); } @@ -1005,6 +1005,12 @@ int mongo_main(int argc, char* argv[]) { true, false); + scope->exec("shellHelper( 'show', 'freeMonitoring' )", + "(freeMonitoring)", + false, + true, + false); + scope->exec("shellHelper( 'show', 'automationNotices' )", "(automationnotices)", false, diff --git a/src/mongo/shell/query.js b/src/mongo/shell/query.js index b43e13c23e8..a5d920cb109 100644 --- a/src/mongo/shell/query.js +++ b/src/mongo/shell/query.js @@ -98,10 +98,6 @@ DBQuery.prototype._canUseCommandCursor = function() { (this._options & DBQuery.Option.exhaust) === 0; }; -DBQuery.prototype._isTailableCursor = function() { - return (this._options & DBQuery.Option.tailable) !== 0; -}; - /** * This method is exposed only for the purpose of testing and should not be used in most contexts. * @@ -305,9 +301,7 @@ DBQuery.prototype.skip = function(skip) { DBQuery.prototype.hasNext = function() { this._exec(); - // Return when limit is reached for tailable cursors. For other cursor options, like an exhaust - // cursor, the server manages closing. - if (this._isTailableCursor() && this._limit > 0 && this._cursorSeen >= this._limit) { + if (this._limit > 0 && this._cursorSeen >= this._limit) { this._cursor.close(); return false; } diff --git a/src/mongo/shell/replsettest.js b/src/mongo/shell/replsettest.js index a22c5142419..827691f822d 100644 --- a/src/mongo/shell/replsettest.js +++ b/src/mongo/shell/replsettest.js @@ -656,9 +656,6 @@ var ReplSetTest = function(opts) { // replica set nodes and return without waiting to connect to any of them. const skipWaitingForAllConnections = (options && options.waitForConnect === false); - // Keep a copy of these options - self.startSetOptions = options; - // Start up without waiting for connections. this.startSetAsync(options, restart); @@ -733,8 +730,8 @@ var ReplSetTest = function(opts) { /** * Blocks until the secondary nodes have completed recovery and their roles are known. Blocks on - * all secondary nodes or just 'secondaries', if specified. Does not wait for all 'newlyAdded' - * fields to be removed by default. + * all secondary nodes or just 'secondaries', if specified. Waits for all 'newlyAdded' fields to + * be removed by default. */ this.awaitSecondaryNodes = function( timeout, secondaries, retryIntervalMS, waitForNewlyAddedRemoval) { @@ -1364,28 +1361,6 @@ var ReplSetTest = function(opts) { cmd[cmdKey] = config; - // If this ReplSet is started using this.startSet and binVersions (ie: - // rst.startSet({binVersion: [...]}) we need to make sure the binVersion combination is - // valid. - if (typeof (this.startSetOptions) === "object" && - this.startSetOptions.hasOwnProperty("binVersion") && - typeof (this.startSetOptions.binVersion) === "object") { - let lastLTSSpecified = false; - let lastContinuousSpecified = false; - this.startSetOptions.binVersion.forEach(function(binVersion, _) { - if (lastLTSSpecified === false) { - lastLTSSpecified = MongoRunner.areBinVersionsTheSame(binVersion, lastLTSFCV); - } - if ((lastContinuousSpecified === false) && (lastLTSFCV !== lastContinuousFCV)) { - lastContinuousSpecified = - MongoRunner.areBinVersionsTheSame(binVersion, lastContinuousFCV); - } - }); - if (lastLTSSpecified && lastContinuousSpecified) { - throw new Error("Can only specify one of 'last-lts' and 'last-continuous' " + - "in binVersion, not both."); - } - } // Initiating a replica set with a single node will use "latest" FCV. This will // cause IncompatibleServerVersion errors if additional "last-lts"/"last-continuous" binary // version nodes are subsequently added to the set, since such nodes cannot set their FCV to @@ -1397,15 +1372,11 @@ var ReplSetTest = function(opts) { Object.keys(this.nodeOptions).forEach(function(key, index) { let val = self.nodeOptions[key]; if (typeof (val) === "object" && val.hasOwnProperty("binVersion")) { - if (lastLTSBinVersionWasSpecifiedForSomeNode === false) { - lastLTSBinVersionWasSpecifiedForSomeNode = - MongoRunner.areBinVersionsTheSame(val.binVersion, lastLTSFCV); - } - if ((lastContinuousBinVersionWasSpecifiedForSomeNode === false) && - (lastLTSFCV !== lastContinuousFCV)) { - lastContinuousBinVersionWasSpecifiedForSomeNode = - MongoRunner.areBinVersionsTheSame(val.binVersion, lastContinuousFCV); - } + lastLTSBinVersionWasSpecifiedForSomeNode = + MongoRunner.areBinVersionsTheSame(val.binVersion, lastLTSFCV); + lastContinuousBinVersionWasSpecifiedForSomeNode = + (lastLTSFCV !== lastContinuousFCV) && + MongoRunner.areBinVersionsTheSame(val.binVersion, lastContinuousFCV); explicitBinVersionWasSpecifiedForSomeNode = true; } }); @@ -1835,19 +1806,13 @@ var ReplSetTest = function(opts) { const timeout = 60 * 1000; this.awaitNodesAgreeOnPrimary(timeout, this.nodes, node); - if (!awaitWritablePrimary) { - return true; - } - // getPrimary() guarantees that there will be only one writable primary for a replica // set. - const newPrimary = this.getPrimary(); - if (newPrimary.host === node.host) { + if (!awaitWritablePrimary || this.getPrimary() === node) { return true; } - jsTest.log(node.host + ' is not primary after stepUp command, ' + newPrimary.host + - ' is the primary'); + jsTest.log(node.host + ' is not primary after stepUp command'); return false; }, "Timed out while waiting for stepUp to succeed on node in port: " + node.port); diff --git a/src/mongo/shell/servers.js b/src/mongo/shell/servers.js index ece24b66a6b..215e7ce3d37 100644 --- a/src/mongo/shell/servers.js +++ b/src/mongo/shell/servers.js @@ -25,6 +25,17 @@ var _parsePath = function() { return dbpath; }; +var _parsePort = function() { + var port = ""; + for (var i = 0; i < arguments.length; ++i) + if (arguments[i] == "--port") + port = arguments[i + 1]; + + if (port == "") + throw Error("No port specified"); + return port; +}; + var createMongoArgs = function(binaryName, args) { if (!Array.isArray(args)) { throw new Error("The second argument to createMongoArgs must be an array"); @@ -71,17 +82,6 @@ MongoRunner.mongosPath = "mongos"; MongoRunner.mongoqPath = "mongoqd"; MongoRunner.mongoShellPath = "mongo"; -MongoRunner.parsePort = function() { - var port = ""; - for (var i = 0; i < arguments.length; ++i) - if (arguments[i] == "--port") - port = arguments[i + 1]; - - if (port == "") - throw Error("No port specified"); - return port; -}; - MongoRunner.VersionSub = function(pattern, version) { this.pattern = pattern; this.version = version; @@ -1380,19 +1380,6 @@ function appendSetParameterArgs(argArray) { argArray.push(...['--setParameter', "logComponentVerbosity=" + logVerbosityParam]); } - if (programMajorMinorVersion >= 530 && - !argArrayContainsSetParameterValue("backtraceLogFile=")) { - let randomName = ""; - let randomStrLen = 20; - const chars = "qwertyuiopasdfghjklzxcvbnm1234567890"; - for (let i = 0; i <= randomStrLen; i++) { - randomName += chars[((Math.random() * 1000) % chars.length) ^ 0]; - } - const backtraceLogFilePath = - MongoRunner.dataDir + "/" + randomName + Date.now() + ".stacktrace"; - argArray.push(...["--setParameter", "backtraceLogFile=" + backtraceLogFilePath]); - } - // When launching a 5.0 mongod, if we're mentioning the // `storeFindAndModifyImagesInSideCollection` setParameter and the corresponding feature // flag is not set, add it for good measure. @@ -1640,7 +1627,7 @@ MongoRunner._startWithArgs = function(argArray, env, waitForConnect) { // TODO: Make there only be one codepath for starting mongo processes argArray = appendSetParameterArgs(argArray); - var port = MongoRunner.parsePort.apply(null, argArray); + var port = _parsePort.apply(null, argArray); var pid = -1; if (env === undefined) { pid = _startMongoProgram.apply(null, argArray); @@ -1675,7 +1662,7 @@ MongoRunner._startWithArgs = function(argArray, env, waitForConnect) { * command line arguments to the program. */ startMongoProgram = function() { - var port = MongoRunner.parsePort.apply(null, arguments); + var port = _parsePort.apply(null, arguments); // Enable test commands. // TODO: Make this work better with multi-version testing so that we can support diff --git a/src/mongo/shell/shardingtest.js b/src/mongo/shell/shardingtest.js index 1319746ea1a..60f6e83fac7 100644 --- a/src/mongo/shell/shardingtest.js +++ b/src/mongo/shell/shardingtest.js @@ -10,7 +10,6 @@ * * { * name {string}: name for this test - * shouldFailInit {boolean}: if set, assert that this will fail initialization * verbose {number}: the verbosity for the mongos * chunkSize {number}: the chunk size to use as configuration for the cluster * @@ -114,9 +113,6 @@ var ShardingTest = function(params) { // concern (5 minutes) var kDefaultWTimeoutMs = 5 * 60 * 1000; - // Oplog collection name - const kOplogName = 'oplog.rs'; - // Ensure we don't mutate the passed-in parameters. params = Object.extend({}, params, true); @@ -408,24 +404,6 @@ var ShardingTest = function(params) { (timeMillis / 1000) + " seconds ***"); }; - this.stopOnFail = function() { - try { - this.stopAllMongos(); - } catch (e) { - print("Did not successfully stop all mongos."); - } - try { - this.stopAllShards(); - } catch (e) { - print("Did not successfully stop all shards."); - } - try { - this.stopAllConfigServers(); - } catch (e) { - print("Did not successfully stop all config servers."); - } - }; - this.adminCommand = function(cmd) { var res = this.admin.runCommand(cmd); if (res && res.ok == 1) @@ -597,12 +575,53 @@ var ShardingTest = function(params) { }; /** - * Waits up to the specified timeout (with a default of 60s) for the collection to be - * considered well balanced. - **/ - this.awaitBalance = function(collName, dbName, timeToWait, interval) { - const coll = this.s.getCollection(dbName + "." + collName); - this.awaitCollectionBalance(coll, timeToWait, interval); + * Waits up to the specified timeout (with a default of 60s) for the balancer to execute one + * round. If no round has been executed, throws an error. + * + * The mongosConnection parameter is optional and allows callers to specify a connection + * different than the first mongos instance in the list. + */ + this.awaitBalancerRound = function(timeoutMs, mongosConnection) { + timeoutMs = timeoutMs || 60000; + mongosConnection = mongosConnection || self.s0; + + // Get the balancer section from the server status of the config server primary + function getBalancerStatus() { + var balancerStatus = + assert.commandWorked(mongosConnection.adminCommand({balancerStatus: 1})); + if (balancerStatus.mode !== 'full') { + throw Error('Balancer is not enabled'); + } + + return balancerStatus; + } + + var initialStatus = getBalancerStatus(); + var currentStatus; + assert.soon( + function() { + currentStatus = getBalancerStatus(); + return (currentStatus.numBalancerRounds - initialStatus.numBalancerRounds) != 0; + }, + function() { + return 'Latest balancer status: ' + tojson(currentStatus); + }, + timeoutMs); + }; + + /** + * Waits up to one minute for the difference in chunks between the most loaded shard and + * least loaded shard to be 0 or 1, indicating that the collection is well balanced. This should + * only be called after creating a big enough chunk difference to trigger balancing. + */ + this.awaitBalance = function(collName, dbName, timeToWait) { + timeToWait = timeToWait || 60000; + + assert.soon(function() { + var x = self.chunkDiff(collName, dbName); + print("chunk diff: " + x); + return x < 2; + }, "no balance happened", timeToWait); }; this.getShard = function(coll, query, includeEmpty) { @@ -995,24 +1014,6 @@ var ShardingTest = function(params) { }; /** - * Waits for all operations to fully replicate on all shards. - */ - this.awaitReplicationOnShards = function() { - this._rs.forEach(replSet => replSet.test.awaitReplication()); - }; - - /** - * Query the oplog from a given node. - */ - ShardingTest.prototype.findOplog = function(conn, query, limit) { - return conn.getDB('local') - .getCollection(kOplogName) - .find(query) - .sort({$natural: -1}) - .limit(limit); - }; - - /** * Returns if there is a new feature compatibility version for the "latest" version. This must * be manually changed if and when there is a new feature compatibility version. */ @@ -1034,7 +1035,8 @@ var ShardingTest = function(params) { assert(isObject(params), 'ShardingTest configuration must be a JSON object'); var testName = params.name || jsTest.name(); - var otherParams = Object.deepMerge(params, params.other || {}); + var otherParams = Object.merge(params, params.other || {}); + var numShards = otherParams.hasOwnProperty('shards') ? otherParams.shards : 2; var mongosVerboseLevel = otherParams.hasOwnProperty('verbose') ? otherParams.verbose : 1; var numMongos = otherParams.hasOwnProperty('mongos') ? otherParams.mongos : 1; @@ -1184,551 +1186,537 @@ var ShardingTest = function(params) { randomSeedAlreadySet = true; } - try { - // - // Start each shard replica set. - // - let startTime = new Date(); // Measure the execution time of startup and initiate. - for (var i = 0; i < numShards; i++) { - var setName = testName + "-rs" + i; - - var rsDefaults = { - useHostname: otherParams.useHostname, - oplogSize: 16, - shardsvr: '', - pathOpts: Object.merge(pathOpts, {shard: i}), - }; + // + // Start each shard replica set. + // + let startTime = new Date(); // Measure the execution time of startup and initiate. + for (var i = 0; i < numShards; i++) { + var setName = testName + "-rs" + i; + + var rsDefaults = { + useHostname: otherParams.useHostname, + oplogSize: 16, + shardsvr: '', + pathOpts: Object.merge(pathOpts, {shard: i}), + }; - if (otherParams.rs || otherParams["rs" + i]) { - if (otherParams.rs) { - rsDefaults = Object.merge(rsDefaults, otherParams.rs); - } - if (otherParams["rs" + i]) { - rsDefaults = Object.merge(rsDefaults, otherParams["rs" + i]); - } - rsDefaults = Object.merge(rsDefaults, otherParams.rsOptions); - rsDefaults.nodes = rsDefaults.nodes || otherParams.numReplicas; - } else { - if (jsTestOptions().shardMixedBinVersions) { - if (!otherParams.shardOptions) { - otherParams.shardOptions = {}; - } - // If the test doesn't depend on specific shard binVersions, create a mixed - // version - // shard cluster that randomly assigns shard binVersions, half "latest" and half - // "last-continuous" or "last-lts". - // shardMixedBinVersions. - if (!otherParams.shardOptions.binVersion) { - Random.setRandomSeed(); - otherParams.shardOptions.binVersion = MongoRunner.versionIterator( - ["latest", jsTestOptions().shardMixedBinVersions], true); - } + if (otherParams.rs || otherParams["rs" + i]) { + if (otherParams.rs) { + rsDefaults = Object.merge(rsDefaults, otherParams.rs); + } + if (otherParams["rs" + i]) { + rsDefaults = Object.merge(rsDefaults, otherParams["rs" + i]); + } + rsDefaults = Object.merge(rsDefaults, otherParams.rsOptions); + rsDefaults.nodes = rsDefaults.nodes || otherParams.numReplicas; + } else { + if (jsTestOptions().shardMixedBinVersions) { + if (!otherParams.shardOptions) { + otherParams.shardOptions = {}; } - - if (otherParams.shardOptions && otherParams.shardOptions.binVersion) { - otherParams.shardOptions.binVersion = - MongoRunner.versionIterator(otherParams.shardOptions.binVersion); + // If the test doesn't depend on specific shard binVersions, create a mixed + // version + // shard cluster that randomly assigns shard binVersions, half "latest" and half + // "last-continuous" or "last-lts". + // shardMixedBinVersions. + if (!otherParams.shardOptions.binVersion) { + Random.setRandomSeed(); + otherParams.shardOptions.binVersion = MongoRunner.versionIterator( + ["latest", jsTestOptions().shardMixedBinVersions], true); } + } - rsDefaults = Object.merge(rsDefaults, otherParams["d" + i]); - rsDefaults = Object.merge(rsDefaults, otherParams.shardOptions); + if (otherParams.shardOptions && otherParams.shardOptions.binVersion) { + otherParams.shardOptions.binVersion = + MongoRunner.versionIterator(otherParams.shardOptions.binVersion); } - rsDefaults.setParameter = rsDefaults.setParameter || {}; - rsDefaults.setParameter.migrationLockAcquisitionMaxWaitMS = - otherParams.migrationLockAcquisitionMaxWaitMS; + rsDefaults = Object.merge(rsDefaults, otherParams["d" + i]); + rsDefaults = Object.merge(rsDefaults, otherParams.shardOptions); + } - var rsSettings = rsDefaults.settings; - delete rsDefaults.settings; + rsDefaults.setParameter = rsDefaults.setParameter || {}; + rsDefaults.setParameter.migrationLockAcquisitionMaxWaitMS = + otherParams.migrationLockAcquisitionMaxWaitMS; - // The number of nodes in the rs field will take priority. - if (otherParams.rs || otherParams["rs" + i]) { - var numReplicas = rsDefaults.nodes || 3; - } else { - var numReplicas = 1; - } - delete rsDefaults.nodes; - - var protocolVersion = rsDefaults.protocolVersion; - delete rsDefaults.protocolVersion; - - var rs = new ReplSetTest({ - name: setName, - nodes: numReplicas, - host: hostName, - useHostName: otherParams.useHostname, - useBridge: otherParams.useBridge, - bridgeOptions: otherParams.bridgeOptions, - keyFile: this.keyFile, - protocolVersion: protocolVersion, - waitForKeys: false, - settings: rsSettings, - seedRandomNumberGenerator: !randomSeedAlreadySet, - }); + var rsSettings = rsDefaults.settings; + delete rsDefaults.settings; - print("ShardingTest starting replica set for shard: " + setName); - - // Start up the replica set but don't wait for it to complete. This allows the startup - // of each shard to proceed in parallel. - this._rs[i] = - {setName: setName, test: rs, nodes: rs.startSetAsync(rsDefaults), url: rs.getURL()}; + // The number of nodes in the rs field will take priority. + if (otherParams.rs || otherParams["rs" + i]) { + var numReplicas = rsDefaults.nodes || 3; + } else { + var numReplicas = 1; } + delete rsDefaults.nodes; - // - // Start up the config server replica set. - // + var protocolVersion = rsDefaults.protocolVersion; + delete rsDefaults.protocolVersion; - var rstOptions = { - useHostName: otherParams.useHostname, + var rs = new ReplSetTest({ + name: setName, + nodes: numReplicas, host: hostName, + useHostName: otherParams.useHostname, useBridge: otherParams.useBridge, bridgeOptions: otherParams.bridgeOptions, keyFile: this.keyFile, + protocolVersion: protocolVersion, waitForKeys: false, - name: testName + "-configRS", + settings: rsSettings, seedRandomNumberGenerator: !randomSeedAlreadySet, - isConfigServer: true, - }; + }); - // always use wiredTiger as the storage engine for CSRS - var startOptions = { - pathOpts: pathOpts, - // Ensure that journaling is always enabled for config servers. - journal: "", - configsvr: "", - storageEngine: "wiredTiger", - }; + print("ShardingTest starting replica set for shard: " + setName); - if (otherParams.configOptions && otherParams.configOptions.binVersion) { - otherParams.configOptions.binVersion = - MongoRunner.versionIterator(otherParams.configOptions.binVersion); - } + // Start up the replica set but don't wait for it to complete. This allows the startup + // of each shard to proceed in parallel. + this._rs[i] = + {setName: setName, test: rs, nodes: rs.startSetAsync(rsDefaults), url: rs.getURL()}; + } - startOptions = Object.merge(startOptions, otherParams.configOptions); - rstOptions = Object.merge(rstOptions, otherParams.configReplSetTestOptions); + // + // Start up the config server replica set. + // + + var rstOptions = { + useHostName: otherParams.useHostname, + host: hostName, + useBridge: otherParams.useBridge, + bridgeOptions: otherParams.bridgeOptions, + keyFile: this.keyFile, + waitForKeys: false, + name: testName + "-configRS", + seedRandomNumberGenerator: !randomSeedAlreadySet, + isConfigServer: true, + }; - var nodeOptions = []; - for (var i = 0; i < numConfigs; ++i) { - nodeOptions.push(otherParams["c" + i] || {}); - } + // always use wiredTiger as the storage engine for CSRS + var startOptions = { + pathOpts: pathOpts, + // Ensure that journaling is always enabled for config servers. + journal: "", + configsvr: "", + storageEngine: "wiredTiger", + }; - rstOptions.nodes = nodeOptions; + if (otherParams.configOptions && otherParams.configOptions.binVersion) { + otherParams.configOptions.binVersion = + MongoRunner.versionIterator(otherParams.configOptions.binVersion); + } - // Start the config server's replica set without waiting for it to complete. This allows it - // to proceed in parallel with the startup of each shard. - this.configRS = new ReplSetTest(rstOptions); - this.configRS.startSetAsync(startOptions); + startOptions = Object.merge(startOptions, otherParams.configOptions); + rstOptions = Object.merge(rstOptions, otherParams.configReplSetTestOptions); - // - // Wait for each shard replica set to finish starting up. - // - for (let i = 0; i < numShards; i++) { - print("Waiting for shard " + this._rs[i].setName + " to finish starting up."); - this._rs[i].test.startSetAwait(); - } - - // - // Wait for the config server to finish starting up. - // - print("Waiting for the config server to finish starting up."); - this.configRS.startSetAwait(); - var config = this.configRS.getReplSetConfig(); - config.configsvr = true; - config.settings = config.settings || {}; - - print("ShardingTest startup for all nodes took " + (new Date() - startTime) + "ms with " + - this.configRS.nodeList().length + " config server nodes and " + totalNumShardNodes() + - " total shard nodes."); - - // - // Initiate each shard replica set and wait for replication. Also initiate the config - // replica set. Whenever possible, in parallel. - // - const shardsRS = this._rs.map(obj => obj.test); - const replicaSetsToInitiate = [...shardsRS, this.configRS].map(rst => { - const rstConfig = rst.getReplSetConfig(); - - // The mongo shell cannot authenticate as the internal __system user in tests that use - // x509 for cluster authentication. Choosing the default value for - // wcMajorityJournalDefault in ReplSetTest cannot be done automatically without the - // shell performing such authentication, so allow tests to pass the value in. - if (otherParams.hasOwnProperty("writeConcernMajorityJournalDefault")) { - rstConfig.writeConcernMajorityJournalDefault = - otherParams.writeConcernMajorityJournalDefault; - } + var nodeOptions = []; + for (var i = 0; i < numConfigs; ++i) { + nodeOptions.push(otherParams["c" + i] || {}); + } - if (rst === this.configRS) { - rstConfig.configsvr = true; - rstConfig.writeConcernMajorityJournalDefault = true; - } + rstOptions.nodes = nodeOptions; - return { - rst, - // Arguments for creating instances of each replica set within parallel threads. - rstArgs: { - name: rst.name, - nodeHosts: rst.nodes.map(node => `127.0.0.1:${node.port}`), - nodeOptions: rst.nodeOptions, - // Mixed-mode SSL tests may specify a keyFile per replica set rather than one - // for the whole cluster. - keyFile: rst.keyFile ? rst.keyFile : this.keyFile, - host: otherParams.useHostname ? hostName : "localhost", - waitForKeys: false, - }, - // Replica set configuration for initiating the replica set. - rstConfig, - }; - }); + // Start the config server's replica set without waiting for it to complete. This allows it + // to proceed in parallel with the startup of each shard. + this.configRS = new ReplSetTest(rstOptions); + this.configRS.startSetAsync(startOptions); - const initiateReplicaSet = (rst, rstConfig) => { - rst.initiateWithAnyNodeAsPrimary(rstConfig); + // + // Wait for each shard replica set to finish starting up. + // + for (let i = 0; i < numShards; i++) { + print("Waiting for shard " + this._rs[i].setName + " to finish starting up."); + this._rs[i].test.startSetAwait(); + } - // Do replication. - rst.awaitNodesAgreeOnPrimary(); - rst.getPrimary().getDB("admin").foo.save({x: 1}); - if (rst.keyFile) { - authutil.asCluster(rst.nodes, rst.keyFile, function() { - rst.awaitReplication(); - }); - } - rst.awaitSecondaryNodes(); + // + // Wait for the config server to finish starting up. + // + print("Waiting for the config server to finish starting up."); + this.configRS.startSetAwait(); + var config = this.configRS.getReplSetConfig(); + config.configsvr = true; + config.settings = config.settings || {}; + + print("ShardingTest startup for all nodes took " + (new Date() - startTime) + "ms with " + + this.configRS.nodeList().length + " config server nodes and " + totalNumShardNodes() + + " total shard nodes."); + + // + // Initiate each shard replica set and wait for replication. Also initiate the config replica + // set. Whenever possible, in parallel. + // + const shardsRS = this._rs.map(obj => obj.test); + const replicaSetsToInitiate = [...shardsRS, this.configRS].map(rst => { + const rstConfig = rst.getReplSetConfig(); + + // The mongo shell cannot authenticate as the internal __system user in tests that use x509 + // for cluster authentication. Choosing the default value for wcMajorityJournalDefault in + // ReplSetTest cannot be done automatically without the shell performing such + // authentication, so allow tests to pass the value in. + if (otherParams.hasOwnProperty("writeConcernMajorityJournalDefault")) { + rstConfig.writeConcernMajorityJournalDefault = + otherParams.writeConcernMajorityJournalDefault; + } + + if (rst === this.configRS) { + rstConfig.configsvr = true; + rstConfig.writeConcernMajorityJournalDefault = true; + } + + return { + rst, + // Arguments for creating instances of each replica set within parallel threads. + rstArgs: { + name: rst.name, + nodeHosts: rst.nodes.map(node => `127.0.0.1:${node.port}`), + nodeOptions: rst.nodeOptions, + // Mixed-mode SSL tests may specify a keyFile per replica set rather than one for + // the whole cluster. + keyFile: rst.keyFile ? rst.keyFile : this.keyFile, + host: otherParams.useHostname ? hostName : "localhost", + waitForKeys: false, + }, + // Replica set configuration for initiating the replica set. + rstConfig, }; + }); - const isParallelSupported = (() => { - if (!tryLoadParallelTester()) { - return false; - } + const initiateReplicaSet = (rst, rstConfig) => { + rst.initiateWithAnyNodeAsPrimary(rstConfig); - for (let {rst} of replicaSetsToInitiate) { - if (rst.startOptions && rst.startOptions.clusterAuthMode === "x509") { - // The mongo shell performing X.509 authentication as a cluster member requires - // starting a parallel shell and using the server's (not the client's) - // certificate. The ReplSetTest instance constructed in a Thread wouldn't have - // copied the path to the server's certificate. We therefore fall back to - // initiating the CSRS and replica set shards sequentially when X.509 - // authentication is being used. - return false; - } + // Do replication. + rst.awaitNodesAgreeOnPrimary(); + rst.getPrimary().getDB("admin").foo.save({x: 1}); + if (rst.keyFile) { + authutil.asCluster(rst.nodes, rst.keyFile, function() { + rst.awaitReplication(); + }); + } + rst.awaitSecondaryNodes(); + }; - for (let n of Object.keys(rst.nodeOptions)) { - const nodeOptions = rst.nodeOptions[n]; - if (nodeOptions && nodeOptions.clusterAuthMode === "x509") { - return false; - } - } - } + const isParallelSupported = (() => { + if (!tryLoadParallelTester()) { + return false; + } - return true; - })(); + for (let {rst} of replicaSetsToInitiate) { + if (rst.startOptions && rst.startOptions.clusterAuthMode === "x509") { + // The mongo shell performing X.509 authentication as a cluster member requires + // starting a parallel shell and using the server's (not the client's) certificate. + // The ReplSetTest instance constructed in a Thread wouldn't have copied the path to + // the server's certificate. We therefore fall back to initiating the CSRS and + // replica set shards sequentially when X.509 authentication is being used. + return false; + } - if (isParallelSupported) { - const threads = []; - try { - for (let {rstArgs, rstConfig} of replicaSetsToInitiate) { - const thread = new Thread((rstArgs, rstConfig, initiateReplicaSet) => { - try { - const rst = new ReplSetTest({rstArgs}); - initiateReplicaSet(rst, rstConfig); - return {ok: 1}; - } catch (e) { - return { - ok: 0, - hosts: rstArgs.nodeHosts, - name: rstArgs.name, - error: e.toString(), - stack: e.stack, - }; - } - }, rstArgs, rstConfig, initiateReplicaSet); - thread.start(); - threads.push(thread); + for (let n of Object.keys(rst.nodeOptions)) { + const nodeOptions = rst.nodeOptions[n]; + if (nodeOptions && nodeOptions.clusterAuthMode === "x509") { + return false; } - } finally { - // Wait for each thread to finish. Throw an error if any thread fails. - const returnData = threads.map(thread => { - thread.join(); - return thread.returnData(); - }); - - returnData.forEach(res => { - assert.commandWorked( - res, 'Initiating shard or config servers as a replica set failed'); - }); - } - } else { - for (let {rst, rstConfig} of replicaSetsToInitiate) { - initiateReplicaSet(rst, rstConfig); } } - for (let i = 0; i < numShards; i++) { - let rs = this._rs[i].test; - - this["rs" + i] = rs; - this._rsObjects[i] = rs; - - this._connections.push(null); + return true; + })(); - let rsConn = new Mongo(rs.getURL()); - rsConn.name = rs.getURL(); + if (isParallelSupported) { + const threads = []; + try { + for (let {rstArgs, rstConfig} of replicaSetsToInitiate) { + const thread = new Thread((rstArgs, rstConfig, initiateReplicaSet) => { + try { + const rst = new ReplSetTest({rstArgs}); + initiateReplicaSet(rst, rstConfig); + return {ok: 1}; + } catch (e) { + return { + ok: 0, + hosts: rstArgs.nodeHosts, + name: rstArgs.name, + error: e.toString(), + stack: e.stack, + }; + } + }, rstArgs, rstConfig, initiateReplicaSet); + thread.start(); + threads.push(thread); + } + } finally { + // Wait for each thread to finish. Throw an error if any thread fails. + const returnData = threads.map(thread => { + thread.join(); + return thread.returnData(); + }); - this._connections[i] = rsConn; - this["shard" + i] = rsConn; - rsConn.rs = rs; + returnData.forEach(res => { + assert.commandWorked(res, + 'Initiating shard or config servers as a replica set failed'); + }); + } + } else { + for (let {rst, rstConfig} of replicaSetsToInitiate) { + initiateReplicaSet(rst, rstConfig); } + } - // Wait for master to be elected before starting mongos - this.configRS.awaitNodesAgreeOnPrimary(); - var csrsPrimary = this.configRS.getPrimary(); + for (let i = 0; i < numShards; i++) { + let rs = this._rs[i].test; - print("ShardingTest startup and initiation for all nodes took " + (new Date() - startTime) + - "ms with " + this.configRS.nodeList().length + " config server nodes and " + - totalNumShardNodes() + " total shard nodes."); + this["rs" + i] = rs; + this._rsObjects[i] = rs; - // If 'otherParams.mongosOptions.binVersion' is an array value, then we'll end up - // constructing a version iterator. - const mongosOptions = []; - for (var i = 0; i < numMongos; ++i) { - let options = { - useHostname: otherParams.useHostname, - pathOpts: Object.merge(pathOpts, {mongos: i}), - verbose: mongosVerboseLevel, - keyFile: this.keyFile, - }; + this._connections.push(null); - if (otherParams.mongosOptions && otherParams.mongosOptions.binVersion) { - otherParams.mongosOptions.binVersion = - MongoRunner.versionIterator(otherParams.mongosOptions.binVersion); - } + let rsConn = new Mongo(rs.getURL()); + rsConn.name = rs.getURL(); - options = Object.merge(options, otherParams.mongosOptions); - options = Object.merge(options, otherParams["s" + i]); - - // The default time for mongos quiesce mode in response to SIGTERM is 15 seconds. - // Reduce this to 0 for faster shutdown. - options.setParameter = options.setParameter || {}; - options.setParameter.mongosShutdownTimeoutMillisForSignaledShutdown = - options.setParameter.mongosShutdownTimeoutMillisForSignaledShutdown || 0; + this._connections[i] = rsConn; + this["shard" + i] = rsConn; + rsConn.rs = rs; + } - options.port = options.port || _allocatePortForMongos(); + // Wait for master to be elected before starting mongos + this.configRS.awaitNodesAgreeOnPrimary(); + var csrsPrimary = this.configRS.getPrimary(); + + print("ShardingTest startup and initiation for all nodes took " + (new Date() - startTime) + + "ms with " + this.configRS.nodeList().length + " config server nodes and " + + totalNumShardNodes() + " total shard nodes."); + + // If 'otherParams.mongosOptions.binVersion' is an array value, then we'll end up constructing a + // version iterator. + const mongosOptions = []; + for (var i = 0; i < numMongos; ++i) { + let options = { + useHostname: otherParams.useHostname, + pathOpts: Object.merge(pathOpts, {mongos: i}), + verbose: mongosVerboseLevel, + keyFile: this.keyFile, + }; - mongosOptions.push(options); + if (otherParams.mongosOptions && otherParams.mongosOptions.binVersion) { + otherParams.mongosOptions.binVersion = + MongoRunner.versionIterator(otherParams.mongosOptions.binVersion); } - const configRS = this.configRS; - const clusterVersionInfo = this.getClusterVersionInfo(); - if (_hasNewFeatureCompatibilityVersion() && clusterVersionInfo.isMixedVersion) { - const fcv = binVersionToFCV(clusterVersionInfo.oldestBinVersion); - function setFeatureCompatibilityVersion() { - assert.commandWorked(csrsPrimary.adminCommand( - {setFeatureCompatibilityVersion: fcv, fromConfigServer: true})); + options = Object.merge(options, otherParams.mongosOptions); + options = Object.merge(options, otherParams["s" + i]); - // Wait for the new featureCompatibilityVersion to propagate to all nodes in the - // CSRS to ensure that older versions of mongos can successfully connect. - configRS.awaitReplication(); - } + // The default time for mongos quiesce mode in response to SIGTERM is 15 seconds. + // Reduce this to 0 for faster shutdown. + options.setParameter = options.setParameter || {}; + options.setParameter.mongosShutdownTimeoutMillisForSignaledShutdown = + options.setParameter.mongosShutdownTimeoutMillisForSignaledShutdown || 0; - if (this.keyFile) { - authutil.asCluster( - this.configRS.nodes, this.keyFile, setFeatureCompatibilityVersion); - } else { - setFeatureCompatibilityVersion(); - } - } + options.port = options.port || _allocatePortForMongos(); - // If chunkSize has been requested for this test, write the configuration - if (otherParams.chunkSize) { - function setChunkSize() { - assert.commandWorked(csrsPrimary.getDB('config').settings.update( - {_id: 'chunksize'}, - {$set: {value: otherParams.chunkSize}}, - {upsert: true, writeConcern: {w: 'majority', wtimeout: kDefaultWTimeoutMs}})); + mongosOptions.push(options); + } - configRS.awaitLastOpCommitted(); - } + const configRS = this.configRS; + const clusterVersionInfo = this.getClusterVersionInfo(); + if (_hasNewFeatureCompatibilityVersion() && clusterVersionInfo.isMixedVersion) { + const fcv = binVersionToFCV(clusterVersionInfo.oldestBinVersion); + function setFeatureCompatibilityVersion() { + assert.commandWorked(csrsPrimary.adminCommand( + {setFeatureCompatibilityVersion: fcv, fromConfigServer: true})); - if (this.keyFile) { - authutil.asCluster(csrsPrimary, this.keyFile, setChunkSize); - } else { - setChunkSize(); - } + // Wait for the new featureCompatibilityVersion to propagate to all nodes in the CSRS + // to ensure that older versions of mongos can successfully connect. + configRS.awaitReplication(); } - this._configDB = this.configRS.getURL(); - for (var i = 0; i < numConfigs; ++i) { - var conn = this.configRS.nodes[i]; - this["config" + i] = conn; - this["c" + i] = conn; + if (this.keyFile) { + authutil.asCluster(this.configRS.nodes, this.keyFile, setFeatureCompatibilityVersion); + } else { + setFeatureCompatibilityVersion(); } + } - printjson('Config servers: ' + this._configDB); + // If chunkSize has been requested for this test, write the configuration + if (otherParams.chunkSize) { + function setChunkSize() { + assert.commandWorked(csrsPrimary.getDB('config').settings.update( + {_id: 'chunksize'}, + {$set: {value: otherParams.chunkSize}}, + {upsert: true, writeConcern: {w: 'majority', wtimeout: kDefaultWTimeoutMs}})); - print("ShardingTest " + this._testName + " :\n" + - tojson({config: this._configDB, shards: this._connections})); + configRS.awaitLastOpCommitted(); + } - this._mongos = []; + if (this.keyFile) { + authutil.asCluster(csrsPrimary, this.keyFile, setChunkSize); + } else { + setChunkSize(); + } + } - // Start the MongoS servers - for (var i = 0; i < numMongos; i++) { - const options = mongosOptions[i]; - options.configdb = this._configDB; + this._configDB = this.configRS.getURL(); + for (var i = 0; i < numConfigs; ++i) { + var conn = this.configRS.nodes[i]; + this["config" + i] = conn; + this["c" + i] = conn; + } - if (otherParams.useBridge) { - var bridgeOptions = - Object.merge(otherParams.bridgeOptions, options.bridgeOptions || {}); - bridgeOptions = Object.merge(bridgeOptions, { - hostName: otherParams.useHostname ? hostName : "localhost", - port: _allocatePortForBridgeForMongos(), - // The mongos processes identify themselves to mongobridge as host:port, where - // the host is the actual hostname of the machine and not localhost. - dest: hostName + ":" + options.port, - }); + printjson('Config servers: ' + this._configDB); - var bridge = new MongoBridge(bridgeOptions); - } + print("ShardingTest " + this._testName + " :\n" + + tojson({config: this._configDB, shards: this._connections})); - var conn = MongoRunner.runMongos(options); - if (!conn) { - throw new Error("Failed to start mongos " + i); - } + this._mongos = []; - if (otherParams.causallyConsistent) { - conn.setCausalConsistency(true); - } + // Start the MongoS servers + for (var i = 0; i < numMongos; i++) { + const options = mongosOptions[i]; + options.configdb = this._configDB; - if (otherParams.useBridge) { - bridge.connectToBridge(); - this._mongos.push(bridge); - unbridgedMongos.push(conn); - } else { - this._mongos.push(conn); - } + if (otherParams.useBridge) { + var bridgeOptions = + Object.merge(otherParams.bridgeOptions, options.bridgeOptions || {}); + bridgeOptions = Object.merge(bridgeOptions, { + hostName: otherParams.useHostname ? hostName : "localhost", + port: _allocatePortForBridgeForMongos(), + // The mongos processes identify themselves to mongobridge as host:port, where the + // host is the actual hostname of the machine and not localhost. + dest: hostName + ":" + options.port, + }); - if (i === 0) { - this.s = this._mongos[i]; - this.admin = this._mongos[i].getDB('admin'); - this.config = this._mongos[i].getDB('config'); - } + var bridge = new MongoBridge(bridgeOptions); + } - this["s" + i] = this._mongos[i]; + var conn = MongoRunner.runMongos(options); + if (!conn) { + throw new Error("Failed to start mongos " + i); } - _extendWithShMethods(); + if (otherParams.causallyConsistent) { + conn.setCausalConsistency(true); + } - // If auth is enabled for the test, login the mongos connections as system in order to - // configure the instances and then log them out again. - if (this.keyFile) { - authutil.asCluster(this._mongos, this.keyFile, _configureCluster); - } else if (mongosOptions[0] && mongosOptions[0].keyFile) { - authutil.asCluster(this._mongos, mongosOptions[0].keyFile, _configureCluster); + if (otherParams.useBridge) { + bridge.connectToBridge(); + this._mongos.push(bridge); + unbridgedMongos.push(conn); } else { - _configureCluster(); - // Ensure that all config server nodes are up to date with any changes made to balancer - // settings before adding shards to the cluster. This prevents shards, which read - // config.settings with readPreference 'nearest', from accidentally fetching stale - // values from secondaries that aren't up-to-date. - this.configRS.awaitLastOpCommitted(); + this._mongos.push(conn); } - try { - if (!otherParams.manualAddShard) { - var testName = this._testName; - var admin = this.admin; + if (i === 0) { + this.s = this._mongos[i]; + this.admin = this._mongos[i].getDB('admin'); + this.config = this._mongos[i].getDB('config'); + } - this._connections.forEach(function(z) { - var n = z.name || z.host || z; + this["s" + i] = this._mongos[i]; + } - print("ShardingTest " + testName + " going to add shard : " + n); + _extendWithShMethods(); - var result = assert.commandWorked(admin.runCommand({addshard: n}), - "Failed to add shard " + n); - z.shardName = result.shardAdded; - }); - } - } catch (e) { - // Clean up the running procceses on failure - print("Failed to add shards, stopping cluster."); - this.stop(); - throw e; - } - - // Ensure that the sessions collection exists so jstests can run things with - // logical sessions and test them. We do this by forcing an immediate cache refresh - // on the config server, which auto-shards the collection for the cluster. - this.configRS.getPrimary().getDB("admin").runCommand({refreshLogicalSessionCacheNow: 1}); - - // Ensure that all CSRS nodes are up to date. This is strictly needed for tests that use - // multiple mongoses. In those cases, the first mongos initializes the contents of the - // 'config' database, but without waiting for those writes to replicate to all the config - // servers then the secondary mongoses risk reading from a stale config server and seeing an - // empty config database. + // If auth is enabled for the test, login the mongos connections as system in order to configure + // the instances and then log them out again. + if (this.keyFile) { + authutil.asCluster(this._mongos, this.keyFile, _configureCluster); + } else if (mongosOptions[0] && mongosOptions[0].keyFile) { + authutil.asCluster(this._mongos, mongosOptions[0].keyFile, _configureCluster); + } else { + _configureCluster(); + // Ensure that all config server nodes are up to date with any changes made to balancer + // settings before adding shards to the cluster. This prevents shards, which read + // config.settings with readPreference 'nearest', from accidentally fetching stale values + // from secondaries that aren't up-to-date. this.configRS.awaitLastOpCommitted(); + } + + try { + if (!otherParams.manualAddShard) { + var testName = this._testName; + var admin = this.admin; + + this._connections.forEach(function(z) { + var n = z.name || z.host || z; - if (jsTestOptions().keyFile) { - jsTest.authenticateNodes(this._mongos); + print("ShardingTest " + testName + " going to add shard : " + n); + + var result = assert.commandWorked(admin.runCommand({addshard: n}), + "Failed to add shard " + n); + z.shardName = result.shardAdded; + }); } + } catch (e) { + // Clean up the running procceses on failure + print("Failed to add shards, stopping cluster."); + this.stop(); + throw e; + } - // Flushes the routing table cache on connection 'conn'. If 'keyFileLocal' is defined, - // authenticates the keyfile user. - const flushRT = function flushRoutingTableAndHandleAuth(conn, keyFileLocal) { - // Invokes the actual execution of cache refresh. - const execFlushRT = (conn) => { - assert.commandWorked(conn.getDB("admin").runCommand( - {_flushRoutingTableCacheUpdates: "config.system.sessions"})); - }; + // Ensure that the sessions collection exists so jstests can run things with + // logical sessions and test them. We do this by forcing an immediate cache refresh + // on the config server, which auto-shards the collection for the cluster. + this.configRS.getPrimary().getDB("admin").runCommand({refreshLogicalSessionCacheNow: 1}); - const x509AuthRequired = (conn.fullOptions && conn.fullOptions.clusterAuthMode && - conn.fullOptions.clusterAuthMode === "x509"); - - if (keyFileLocal) { - authutil.asCluster(conn, keyFileLocal, () => execFlushRT(conn)); - } else if (x509AuthRequired) { - const exitCode = - _runMongoProgram(...["mongo", - conn.host, - "--tls", - "--tlsAllowInvalidHostnames", - "--tlsCertificateKeyFile", - conn.fullOptions.tlsCertificateKeyFile - ? conn.fullOptions.tlsCertificateKeyFile - : conn.fullOptions.sslPEMKeyFile, - "--tlsCAFile", - conn.fullOptions.tlsCAFile ? conn.fullOptions.tlsCAFile - : conn.fullOptions.sslCAFile, - "--authenticationDatabase=$external", - "--authenticationMechanism=MONGODB-X509", - "--eval", - `(${execFlushRT.toString()})(db.getMongo())`, - ]); - assert.eq(0, exitCode, "parallel shell for x509 auth failed"); - } else { - execFlushRT(conn); - } - }; + // Ensure that all CSRS nodes are up to date. This is strictly needed for tests that use + // multiple mongoses. In those cases, the first mongos initializes the contents of the 'config' + // database, but without waiting for those writes to replicate to all the config servers then + // the secondary mongoses risk reading from a stale config server and seeing an empty config + // database. + this.configRS.awaitLastOpCommitted(); - if (!otherParams.manualAddShard) { - for (let i = 0; i < numShards; i++) { - const keyFileLocal = - (otherParams.shards && otherParams.shards[i] && otherParams.shards[i].keyFile) - ? otherParams.shards[i].keyFile - : this.keyFile; - - const rs = this._rs[i].test; - flushRT(rs.getPrimary(), keyFileLocal); - } + if (jsTestOptions().keyFile) { + jsTest.authenticateNodes(this._mongos); + } + + // Flushes the routing table cache on connection 'conn'. If 'keyFileLocal' is defined, + // authenticates the keyfile user. + const flushRT = function flushRoutingTableAndHandleAuth(conn, keyFileLocal) { + // Invokes the actual execution of cache refresh. + const execFlushRT = (conn) => { + assert.commandWorked(conn.getDB("admin").runCommand( + {_flushRoutingTableCacheUpdates: "config.system.sessions"})); + }; - self.waitForShardingInitialized(); + const x509AuthRequired = (conn.fullOptions && conn.fullOptions.clusterAuthMode && + conn.fullOptions.clusterAuthMode === "x509"); + + if (keyFileLocal) { + authutil.asCluster(conn, keyFileLocal, () => execFlushRT(conn)); + } else if (x509AuthRequired) { + const exitCode = _runMongoProgram( + ...["mongo", + conn.host, + "--tls", + "--tlsAllowInvalidHostnames", + "--tlsCertificateKeyFile", + conn.fullOptions.tlsCertificateKeyFile ? conn.fullOptions.tlsCertificateKeyFile + : conn.fullOptions.sslPEMKeyFile, + "--tlsCAFile", + conn.fullOptions.tlsCAFile ? conn.fullOptions.tlsCAFile + : conn.fullOptions.sslCAFile, + "--authenticationDatabase=$external", + "--authenticationMechanism=MONGODB-X509", + "--eval", + `(${execFlushRT.toString()})(db.getMongo())`, + ]); + assert.eq(0, exitCode, "parallel shell for x509 auth failed"); + } else { + execFlushRT(conn); } - } catch (e) { - // this was expected to fail, so clean up appropriately - if (params.shouldFailInit === true) { - this.stopOnFail(); + }; + + if (!otherParams.manualAddShard) { + for (let i = 0; i < numShards; i++) { + const keyFileLocal = + (otherParams.shards && otherParams.shards[i] && otherParams.shards[i].keyFile) + ? otherParams.shards[i].keyFile + : this.keyFile; + + const rs = this._rs[i].test; + flushRT(rs.getPrimary(), keyFileLocal); } - throw e; + + self.waitForShardingInitialized(); } - // This initialization was expected to fail, but it did not. - assert.neq( - true, params.shouldFailInit, "This was expected to fail initialization, but it did not"); }; // Stub for a hook to check that collection UUIDs are consistent across shards and the config diff --git a/src/mongo/shell/shell_utils.cpp b/src/mongo/shell/shell_utils.cpp index abdbb179bfa..cb82f223f9b 100644 --- a/src/mongo/shell/shell_utils.cpp +++ b/src/mongo/shell/shell_utils.cpp @@ -521,23 +521,15 @@ BSONObj numberDecimalsAlmostEqual(const BSONObj& input, void*) { auto ten = Decimal128(10); auto exponent = a.toAbs().logarithm(ten).round(); - // Early exit for zero, infinity and NaN cases. - if ((a.isZero() && b.isZero()) || (a.isNaN() && b.isNaN()) || - (a.isInfinite() && b.isInfinite() && (a.isNegative() == b.isNegative()))) { - return BSON("" << true /* isErrorAcceptable */); - } else if (!a.isZero() && !b.isZero()) { - // Return early if arguments are not the same order of magnitude. - if (exponent != b.toAbs().logarithm(ten).round()) { - return BSON("" << false); - } - - // Put the whole number behind the decimal point. - if (!exponent.isZero()) { - a = a.divide(ten.power(exponent)); - b = b.divide(ten.power(exponent)); - } + // Return early if arguments are not the same order of magnitude. + if (exponent != b.toAbs().logarithm(ten).round()) { + return BSON("" << false); } + // Put the whole number behind the decimal point. + a = a.divide(ten.power(exponent)); + b = b.divide(ten.power(exponent)); + auto places = third.numberDecimal(); auto isErrorAcceptable = a.subtract(b) .toAbs() diff --git a/src/mongo/shell/types.js b/src/mongo/shell/types.js index ce8fd02b172..c06601f9161 100644 --- a/src/mongo/shell/types.js +++ b/src/mongo/shell/types.js @@ -292,36 +292,6 @@ Object.merge = function(dst, src, deep) { return Object.extend(clone, src, deep); }; -// If there is a conflict in values of a key for two objects being merged, the second value will -// override the first one in the merged object -Object.deepMerge = function(...objects) { - const isObject = obj => obj && typeof obj === 'object'; - - // Create new object prev to hold combination of all object fields. - return objects.reduce((prev, obj) => { - if (obj === undefined) { - obj = {}; - } - Object.keys(obj).forEach(key => { - const pVal = prev[key]; // Get the values for key from the two objects being merged. - const oVal = obj[key]; - - if (Array.isArray(pVal) && - Array.isArray(oVal)) { // If both are arrays then concatenate them into a new - // array and add it to prev. - prev[key] = pVal.concat(...oVal); - } else if (isObject(pVal) && - isObject(oVal)) { // If both are objects then recursively merge again. - prev[key] = Object.deepMerge(pVal, oVal); - } else { // In all other cases set prev[key] to obj[key]. - prev[key] = oVal; - } - }); - - return prev; - }, {}); -}; - Object.keySet = function(o) { var ret = new Array(); for (var i in o) { diff --git a/src/mongo/shell/utils.js b/src/mongo/shell/utils.js index e1e09a83a7b..ebfa03f7268 100644 --- a/src/mongo/shell/utils.js +++ b/src/mongo/shell/utils.js @@ -29,13 +29,6 @@ function _getErrorWithCode(codeOrObj, message) { if (codeOrObj.hasOwnProperty("writeErrors")) { e.writeErrors = codeOrObj.writeErrors; - } else if ((codeOrObj instanceof BulkWriteResult || codeOrObj instanceof BulkWriteError) && - codeOrObj.hasWriteErrors()) { - e.writeErrors = codeOrObj.getWriteErrors(); - } - - if (codeOrObj instanceof WriteResult && codeOrObj.hasWriteError()) { - e.writeErrors = [codeOrObj.getWriteError()]; } if (codeOrObj.hasOwnProperty("errorLabels")) { @@ -92,11 +85,7 @@ function isNetworkError(errorOrResponse) { "error doing query", "socket exception", "SocketException", - "HostNotFound", - "HostUnreachable", - "NetworkTimeout", - "ConnectionPoolExpired", - "ConnectionError" + "HostNotFound" ]; // Then check if it's an Error, if so see if any of the known network error strings appear @@ -1141,6 +1130,52 @@ shellHelper.show = function(what) { } } + if (what == "freeMonitoring") { + var dbDeclared, ex; + try { + // !!db essentially casts db to a boolean + // Will throw a reference exception if db hasn't been declared. + dbDeclared = !!db; + } catch (ex) { + dbDeclared = false; + } + + if (dbDeclared) { + const freemonStatus = db.adminCommand({getFreeMonitoringStatus: 1}); + + if (freemonStatus.ok) { + if (freemonStatus.state == 'enabled' && + freemonStatus.hasOwnProperty('userReminder')) { + print("---"); + print(freemonStatus.userReminder); + print("---"); + } else if (freemonStatus.state === 'undecided') { + print( + "---\n" + messageIndent + + "Enable MongoDB's free cloud-based monitoring service, which will then receive and display\n" + + messageIndent + + "metrics about your deployment (disk utilization, CPU, operation statistics, etc).\n" + + "\n" + messageIndent + + "The monitoring data will be available on a MongoDB website with a unique URL accessible to you\n" + + messageIndent + + "and anyone you share the URL with. MongoDB may use this information to make product\n" + + messageIndent + + "improvements and to suggest MongoDB products and deployment options to you.\n" + + "\n" + messageIndent + + "To enable free monitoring, run the following command: db.enableFreeMonitoring()\n" + + messageIndent + + "To permanently disable this reminder, run the following command: db.disableFreeMonitoring()\n" + + "---\n"); + } + } + + return ""; + } else { + print("Cannot show freeMonitoring, \"db\" is not set"); + return ""; + } + } + if (what == "nonGenuineMongoDBCheck") { let matchesKnownImposterSignature = false; diff --git a/src/mongo/shell/utils_sh.js b/src/mongo/shell/utils_sh.js index e086bbf8f46..b4bd7175096 100644 --- a/src/mongo/shell/utils_sh.js +++ b/src/mongo/shell/utils_sh.js @@ -96,9 +96,6 @@ sh.help = function() { "returns wheter the specified collection is balanced or the balancer needs to take more actions on it"); print("\tsh.configureCollectionBalancing(fullName, params) " + "configure balancing settings for a specific collection"); - print("\tsh.awaitCollectionBalance(coll) waits for a collection to be balanced"); - print( - "\tsh.verifyCollectionIsBalanced(coll) verifies that a collection is well balanced by checking the actual data size on each shard"); }; sh.status = function(verbose, configDB) { @@ -258,28 +255,18 @@ sh.waitForPingChange = function(activePings, timeout, interval) { return remainingPings; }; -/** - * Waits up to the specified timeout (with a default of 60s) for the balancer to execute one - * round. If no round has been executed, throws an error. - */ -sh.awaitBalancerRound = function(timeout, interval) { - timeout = timeout || 60000; - +sh.waitForBalancer = function(wait, timeout, interval) { + if (typeof (wait) === 'undefined') { + wait = false; + } var initialStatus = sh._getBalancerStatus(); + if (!initialStatus.inBalancerRound && !wait) { + return; + } var currentStatus; assert.soon(function() { currentStatus = sh._getBalancerStatus(); - assert.eq(currentStatus.mode, 'full', "Balancer is disabled"); - if (!friendlyEqual(currentStatus.term, initialStatus.term)) { - // A new primary of the csrs has been elected - initialStatus = currentStatus; - return false; - } - assert.gte(currentStatus.numBalancerRounds, - initialStatus.numBalancerRounds, - 'Number of balancer rounds moved back in time unexpectedly. Current status: ' + - tojson(currentStatus) + ', initial status: ' + tojson(initialStatus)); - return currentStatus.numBalancerRounds > initialStatus.numBalancerRounds; + return (currentStatus.numBalancerRounds - initialStatus.numBalancerRounds) != 0; }, 'Latest balancer status: ' + tojson(currentStatus), timeout, interval); }; @@ -317,118 +304,6 @@ sh.enableBalancing = function(coll) { {writeConcern: {w: 'majority', wtimeout: 60000}})); }; -sh.awaitCollectionBalance = function(coll, timeout, interval) { - if (coll === undefined) { - throw Error("Must specify collection"); - } - timeout = timeout || 60000; - interval = interval || 200; - - const ns = coll.getFullName(); - const orphanDocsPipeline = [ - {'$collStats': {'storageStats': {}}}, - {'$project': {'shard': true, 'storageStats': {'numOrphanDocs': true}}}, - {'$group': {'_id': null, 'totalNumOrphanDocs': {'$sum': '$storageStats.numOrphanDocs'}}} - ]; - - var oldDb = (typeof (db) === 'undefined' ? undefined : db); - try { - db = coll.getDB(); - - assert.soon( - function() { - assert.soon(function() { - return assert - .commandWorked(sh._adminCommand({balancerCollectionStatus: ns}, true)) - .balancerCompliant; - }, 'Timed out waiting for the collection to be balanced', timeout, interval); - - // (SERVER-67301) Wait for orphans counter to be 0 to account for potential stale - // orphans count - sh.disableBalancing(coll); - assert.soon(function() { - return coll.aggregate(orphanDocsPipeline).toArray()[0].totalNumOrphanDocs === 0; - }, 'Timed out waiting for orphans counter to be 0', timeout, interval); - sh.enableBalancing(coll); - - // (SERVER-70602) Wait for some balancing rounds to avoid balancerCollectionStatus - // reporting balancerCompliant too early - for (let i = 0; i < 3; ++i) { - sh.awaitBalancerRound(timeout, interval); - } - - return assert.commandWorked(sh._adminCommand({balancerCollectionStatus: ns}, true)) - .balancerCompliant; - }, - 'Timed out waiting for collection to be balanced and orphans counter to be 0', - timeout, - interval); - } finally { - db = oldDb; - } -}; - -/** - * Verifies if given collection is properly balanced according to the data size aware balancing - * policy - */ -sh.verifyCollectionIsBalanced = function(coll) { - if (coll === undefined) { - throw Error("Must specify collection"); - } - - var oldDb = db; - try { - db = coll.getDB(); - - const configDB = sh._getConfigDB(); - const ns = coll.getFullName(); - const collection = configDB.collections.findOne({_id: ns}); - - let collSizeOnShards = []; - let shards = []; - const collStatsPipeline = [ - {'$collStats': {'storageStats': {}}}, - { - '$project': { - 'shard': true, - 'storageStats': - {'count': true, 'size': true, 'avgObjSize': true, 'numOrphanDocs': true} - } - }, - {'$sort': {'shard': 1}} - ]; - - let kChunkSize = 1024 * 1024 * - assert.commandWorked(sh._adminCommand({balancerCollectionStatus: ns})).chunkSize; - // TODO SERVER-67898 delete kChunkSize overwrite after completing the ticket - if (kChunkSize == 0) { - kChunkSize = collection.maxChunkSizeBytes; - } - - // Get coll size per shard - const storageStats = coll.aggregate(collStatsPipeline).toArray(); - coll.aggregate(collStatsPipeline).forEach((shardStats) => { - shards.push(shardStats['shard']); - const collSize = (shardStats['storageStats']['count'] - - shardStats['storageStats']['numOrphanDocs']) * - shardStats['storageStats']['avgObjSize']; - collSizeOnShards.push(collSize); - }); - - let errorMsg = "Collection not balanced. collection= " + tojson(collection) + - ", shards= " + tojson(shards) + ", collSizeOnShards=" + tojson(collSizeOnShards) + - ", storageStats=" + tojson(storageStats) + ", kChunkSize=" + tojson(kChunkSize); - - assert.lte((Math.max(...collSizeOnShards) - Math.min(...collSizeOnShards)), - 3 * kChunkSize, - errorMsg); - - } finally { - db = oldDb; - } -}; - /* * Can call _lastMigration( coll ), _lastMigration( db ), _lastMigration( st ), _lastMigration( * mongos ) |
