summaryrefslogtreecommitdiff
path: root/jstests
diff options
context:
space:
mode:
Diffstat (limited to 'jstests')
-rw-r--r--jstests/hooks/validate_collections.js6
-rw-r--r--jstests/libs/all_commands_test.js77
-rw-r--r--jstests/replsets/buildindexes_false_with_system_indexes.js1
-rw-r--r--jstests/replsets/db_reads_while_recovering_all_commands.js387
-rw-r--r--jstests/replsets/maintenance2.js5
-rw-r--r--jstests/replsets/maintenance_non-blocking.js3
6 files changed, 478 insertions, 1 deletions
diff --git a/jstests/hooks/validate_collections.js b/jstests/hooks/validate_collections.js
index 31e5d26affe..93fba795625 100644
--- a/jstests/hooks/validate_collections.js
+++ b/jstests/hooks/validate_collections.js
@@ -23,6 +23,12 @@ function validateCollections(db, obj) {
var adminDB = db.getSiblingDB("admin");
+ // Skip validating collections for arbiters.
+ if (adminDB.isMaster('admin').arbiterOnly === true) {
+ print('Skipping collection validation on arbiter for db: ' + tojson(db));
+ return success;
+ }
+
// Don't run validate on view namespaces.
let filter = {type: "collection"};
if (jsTest.options().skipValidationOnInvalidViewDefinitions) {
diff --git a/jstests/libs/all_commands_test.js b/jstests/libs/all_commands_test.js
new file mode 100644
index 00000000000..93f16ec868b
--- /dev/null
+++ b/jstests/libs/all_commands_test.js
@@ -0,0 +1,77 @@
+/**
+ * A library for testing behaviors over the set of all available commands.
+ * Users of this library must declare the expected results for all commands and pass
+ * them into a cmdMap object.
+ *
+ * Each entry in the map should have at least the following fields:
+ * {
+ * command: {<command object, e.g. 'find: test, filter: {a: 1}'>}
+ * skip: <reason string> // optional field
+ * }
+ *
+ * All the logic about how exactly a test should run is defined by the user.
+ * See the 'testAllCommands' function.
+ */
+const AllCommandsTest = (function() {
+ "use strict";
+ /**
+ * Verifies that the command map contains an entry for every command that exists on the server.
+ * This is already called in 'testAllCommands', so there is no need to call this directly.
+ *
+ * @param {Object} conn The shell connection to run the suite over.
+ * @param {map} cmdMap A map of all commands, with their invocations and expected behavior.
+ */
+ function checkCommandCoverage(conn, cmdMap) {
+ const res = assert.commandWorked(conn.adminCommand({listCommands: 1}));
+ const commandsInListCommands = Object.keys(res.commands);
+ const cmdListLen = commandsInListCommands.length;
+ let missingCommands = [];
+
+ // Make sure that all valid commands are covered in the cmdMap.
+ for (var i = 0; i < cmdListLen; i++) {
+ const command = commandsInListCommands[i];
+ if (!cmdMap[command]) {
+ missingCommands.push(command);
+ }
+ }
+ if (missingCommands.length !== 0) {
+ throw new Error("Command map is missing entries for " + missingCommands);
+ }
+
+ return commandsInListCommands;
+ }
+
+ /**
+ * The runner function for this library.
+ * Use the 'skip' option for tests that should not run.
+ *
+ * @param {Object} conn The shell connection to run the suite over.
+ * @param {map} cmdMap A map of all commands, with their invocations and expected behavior.
+ * @param {function} testFn A user-defined function to execute on every command.
+ */
+ function testAllCommands(conn, cmdMap, testFn) {
+ // First check that the map contains all available commands.
+ const commands = checkCommandCoverage(conn, cmdMap);
+
+ for (var i = 0; i < commands.length; i++) {
+ const command = commands[i];
+ const test = cmdMap[command];
+
+ // Coverage already guaranteed above, but check again just in case.
+ assert(test, "Coverage failure: must explicitly define a test for " + command);
+
+ jsTestLog("Running command: " + command);
+
+ if (test.skip !== undefined) {
+ jsTestLog("Skipping " + command + ": " + test.skip);
+ continue;
+ }
+
+ // Run logic specified by caller.
+ jsTestName("Testing " + command);
+ testFn(test);
+ }
+ }
+
+ return {testAllCommands: testAllCommands};
+})(); \ No newline at end of file
diff --git a/jstests/replsets/buildindexes_false_with_system_indexes.js b/jstests/replsets/buildindexes_false_with_system_indexes.js
index cfada55041b..78922e691b2 100644
--- a/jstests/replsets/buildindexes_false_with_system_indexes.js
+++ b/jstests/replsets/buildindexes_false_with_system_indexes.js
@@ -65,6 +65,7 @@
assert.eq(["_id_"], hiddenAdminDb.system.roles.getIndexes().map(x => x.name).sort());
secondary = rst.restart(secondary, {}, true /* wait for node to become healthy */);
+ rst.awaitSecondaryNodes();
secondaryAdminDb = secondary.getDB("admin");
assert.eq(["_id_"], secondaryAdminDb.system.users.getIndexes().map(x => x.name).sort());
assert.eq(["_id_"], secondaryAdminDb.system.roles.getIndexes().map(x => x.name).sort());
diff --git a/jstests/replsets/db_reads_while_recovering_all_commands.js b/jstests/replsets/db_reads_while_recovering_all_commands.js
new file mode 100644
index 00000000000..dfee8382c55
--- /dev/null
+++ b/jstests/replsets/db_reads_while_recovering_all_commands.js
@@ -0,0 +1,387 @@
+/**
+ * This file defines tests for all existing commands and their expected behavior when run against a
+ * node that is in RECOVERING state.
+ *
+ * Tagged as multiversion-incompatible as the list of commands will vary depeding on version.
+ * @tags: [multiversion_incompatible]
+ */
+
+(function() {
+ "use strict";
+
+ // This will verify the completeness of our map and run all tests.
+ load("jstests/libs/all_commands_test.js");
+
+ const name = jsTestName();
+ const dbName = "alltestsdb";
+ const collName = "alltestscoll";
+ const fullNs = dbName + "." + collName;
+
+ // Pre-written reasons for skipping a test.
+ const isAnInternalCommand = "internal command";
+ const isNotAUserDataRead = "does not return user data";
+ const isPrimaryOnly = "primary only";
+
+ const allCommands = {
+ _addShard: {skip: isPrimaryOnly},
+ _cloneCollectionOptionsFromPrimaryShard: {skip: isPrimaryOnly},
+ _configsvrAddShard: {skip: isPrimaryOnly},
+ _configsvrAddShardToZone: {skip: isPrimaryOnly},
+ _configsvrBalancerCollectionStatus: {skip: isPrimaryOnly},
+ _configsvrBalancerStart: {skip: isPrimaryOnly},
+ _configsvrBalancerStatus: {skip: isPrimaryOnly},
+ _configsvrBalancerStop: {skip: isPrimaryOnly},
+ _configsvrClearJumboFlag: {skip: isPrimaryOnly},
+ _configsvrCommitChunkMerge: {skip: isPrimaryOnly},
+ _configsvrCommitChunkMigration: {skip: isPrimaryOnly},
+ _configsvrCommitChunkSplit: {skip: isPrimaryOnly},
+ _configsvrCommitMovePrimary: {skip: isPrimaryOnly},
+ _configsvrCreateCollection: {skip: isPrimaryOnly},
+ _configsvrCreateDatabase: {skip: isPrimaryOnly},
+ _configsvrDropCollection: {skip: isPrimaryOnly},
+ _configsvrDropDatabase: {skip: isPrimaryOnly},
+ _configsvrEnableSharding: {skip: isPrimaryOnly},
+ _configsvrEnsureChunkVersionIsGreaterThan: {skip: isPrimaryOnly},
+ _configsvrMoveChunk: {skip: isPrimaryOnly},
+ _configsvrMovePrimary: {skip: isPrimaryOnly},
+ _configsvrRefineCollectionShardKey: {skip: isPrimaryOnly},
+ _configsvrRemoveShard: {skip: isPrimaryOnly},
+ _configsvrRemoveShardFromZone: {skip: isPrimaryOnly},
+ _configsvrShardCollection: {skip: isPrimaryOnly},
+ _configsvrUpdateZoneKeyRange: {skip: isPrimaryOnly},
+ _flushDatabaseCacheUpdates: {skip: isPrimaryOnly},
+ _flushRoutingTableCacheUpdates: {skip: isPrimaryOnly},
+ _getNextSessionMods: {skip: isPrimaryOnly},
+ _getUserCacheGeneration: {skip: isNotAUserDataRead},
+ _hashBSONElement: {skip: isNotAUserDataRead},
+ _isSelf: {skip: isNotAUserDataRead},
+ _killOperations: {skip: isNotAUserDataRead},
+ _mergeAuthzCollections: {skip: isPrimaryOnly},
+ _migrateClone: {skip: isPrimaryOnly},
+ _recvChunkAbort: {skip: isPrimaryOnly},
+ _recvChunkCommit: {skip: isPrimaryOnly},
+ _recvChunkStart: {skip: isPrimaryOnly},
+ _recvChunkStatus: {skip: isPrimaryOnly},
+ _shardsvrCloneCatalogData: {skip: isPrimaryOnly},
+ _shardsvrMovePrimary: {skip: isPrimaryOnly},
+ _shardsvrShardCollection: {skip: isPrimaryOnly},
+ _transferMods: {skip: isPrimaryOnly},
+ abortTransaction: {skip: isPrimaryOnly},
+ aggregate: {
+ command: {aggregate: collName, pipeline: [{$match: {}}], cursor: {}},
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NotMasterOrSecondary,
+ },
+ appendOplogNote: {skip: isPrimaryOnly},
+ applyOps: {skip: isPrimaryOnly},
+ authenticate: {skip: isNotAUserDataRead},
+ authSchemaUpgrade: {skip: isPrimaryOnly},
+ availableQueryOptions: {skip: isNotAUserDataRead},
+ buildInfo: {skip: isNotAUserDataRead},
+ captrunc: {skip: isPrimaryOnly},
+ checkShardingIndex: {skip: isPrimaryOnly},
+ cleanupOrphaned: {skip: isPrimaryOnly},
+ clearLog: {skip: isNotAUserDataRead},
+ clone: {skip: isPrimaryOnly},
+ cloneCollection: {skip: isPrimaryOnly},
+ cloneCollectionAsCapped: {skip: isPrimaryOnly},
+ collMod: {skip: isPrimaryOnly},
+ collStats: {
+ command: {aggregate: collName, pipeline: [{$collStats: {count: {}}}], cursor: {}},
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NotMasterOrSecondary,
+ },
+ commitTransaction: {skip: isPrimaryOnly},
+ compact: {skip: isNotAUserDataRead},
+ configureFailPoint: {skip: isNotAUserDataRead},
+ connPoolStats: {skip: isNotAUserDataRead},
+ connPoolSync: {skip: isNotAUserDataRead},
+ connectionStatus: {skip: isNotAUserDataRead},
+ convertToCapped: {skip: isPrimaryOnly},
+ coordinateCommitTransaction: {skip: isNotAUserDataRead},
+ copydb: {skip: isPrimaryOnly},
+ copydbgetnonce: {skip: isPrimaryOnly},
+ copydbsaslstart: {skip: isPrimaryOnly},
+ count: {
+ command: {count: collName},
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NotMasterOrSecondary,
+ },
+ cpuload: {skip: isNotAUserDataRead},
+ create: {skip: isPrimaryOnly},
+ createIndexes: {skip: isPrimaryOnly},
+ createRole: {skip: isPrimaryOnly},
+ createUser: {skip: isPrimaryOnly},
+ currentOp: {skip: isNotAUserDataRead},
+ dataSize: {
+ command: {dataSize: fullNs},
+ },
+ dbCheck: {skip: isPrimaryOnly},
+ dbHash: {
+ command: {dbHash: 1},
+ },
+ dbStats: {
+ command: {dbStats: 1},
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NotMasterOrSecondary,
+ },
+ delete: {skip: isPrimaryOnly},
+ distinct: {
+ command: {distinct: collName, key: "a"},
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NotMasterOrSecondary,
+ },
+ driverOIDTest: {skip: isNotAUserDataRead},
+ drop: {skip: isPrimaryOnly},
+ dropAllRolesFromDatabase: {skip: isPrimaryOnly},
+ dropAllUsersFromDatabase: {skip: isPrimaryOnly},
+ dropConnections: {skip: isNotAUserDataRead},
+ dropDatabase: {skip: isPrimaryOnly},
+ dropIndexes: {skip: isPrimaryOnly},
+ dropRole: {skip: isPrimaryOnly},
+ dropUser: {skip: isPrimaryOnly},
+ echo: {skip: isNotAUserDataRead},
+ emptycapped: {skip: isPrimaryOnly},
+ endSessions: {skip: isNotAUserDataRead},
+ eval: {skip: isPrimaryOnly},
+ explain: {
+ command: {count: collName},
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NotMasterOrSecondary,
+ },
+ features: {skip: isNotAUserDataRead},
+ filemd5: {skip: isNotAUserDataRead},
+ find: {
+ command: {find: collName, filter: {a: 1}},
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NotMasterOrSecondary,
+ },
+ findAndModify: {skip: isPrimaryOnly},
+ flushRouterConfig: {skip: isNotAUserDataRead},
+ forceerror: {skip: isNotAUserDataRead},
+ fsync: {skip: isNotAUserDataRead},
+ fsyncUnlock: {skip: isNotAUserDataRead},
+ geoNear: {
+ command: {geoNear: collName, near: {type: "Point", coordinates: [-10, 10]}},
+ spherical: true,
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NotMasterOrSecondary,
+ },
+ geoSearch: {
+ command: {geoSearch: collName, search: {}, near: [-42, 42], maxDistance: 1},
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NotMasterOrSecondary
+ },
+ getCmdLineOpts: {skip: isNotAUserDataRead},
+ getDatabaseVersion: {skip: isNotAUserDataRead},
+ getDefaultRWConcern: {skip: isNotAUserDataRead},
+ getDiagnosticData: {skip: isNotAUserDataRead},
+ getFreeMonitoringStatus: {skip: isNotAUserDataRead},
+ getLastError: {skip: isPrimaryOnly},
+ getLog: {skip: isNotAUserDataRead},
+ getMore: {
+ command: {getMore: NumberLong(123), collection: collName},
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NotMasterOrSecondary
+ },
+ getParameter: {skip: isNotAUserDataRead},
+ getPrevError: {skip: isNotAUserDataRead},
+ getShardMap: {skip: isNotAUserDataRead},
+ getShardVersion: {skip: isPrimaryOnly},
+ getnonce: {skip: isNotAUserDataRead},
+ godinsert: {skip: isAnInternalCommand},
+ grantPrivilegesToRole: {skip: isPrimaryOnly},
+ grantRolesToRole: {skip: isPrimaryOnly},
+ grantRolesToUser: {skip: isPrimaryOnly},
+ group: {
+ command: {group: {ns: collName, key: {a: 1}}},
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NotMasterOrSecondary
+ },
+ handshake: {skip: isNotAUserDataRead},
+ hostInfo: {skip: isNotAUserDataRead},
+ httpClientRequest: {skip: isNotAUserDataRead},
+ insert: {skip: isPrimaryOnly},
+ internalRenameIfOptionsAndIndexesMatch: {skip: isAnInternalCommand},
+ invalidateUserCache: {skip: isNotAUserDataRead},
+ isMaster: {skip: isNotAUserDataRead},
+ journalLatencyTest: {skip: isNotAUserDataRead},
+ killAllSessions: {skip: isNotAUserDataRead},
+ killAllSessionsByPattern: {skip: isNotAUserDataRead},
+ killCursors: {skip: isNotAUserDataRead},
+ killOp: {skip: isNotAUserDataRead},
+ killSessions: {skip: isNotAUserDataRead},
+ listCollections: {
+ command: {listCollections: 1},
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NotMasterOrSecondary
+ },
+ listCommands: {command: {listCommands: 1}},
+ listDatabases: {
+ command: {listDatabases: 1},
+ isAdminCommand: true,
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NotMasterOrSecondary
+ },
+ listIndexes: {
+ command: {listIndexes: collName},
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NotMasterOrSecondary
+ },
+ lockInfo: {skip: isPrimaryOnly},
+ logApplicationMessage: {skip: isNotAUserDataRead},
+ logRotate: {skip: isNotAUserDataRead},
+ logout: {skip: isNotAUserDataRead},
+ makeSnapshot: {skip: isNotAUserDataRead},
+ mapReduce: {
+ command: {
+ mapReduce: collName,
+ map: function() {},
+ reduce: function(key, vals) {},
+ out: {inline: 1}
+ },
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NotMasterOrSecondary,
+ },
+ "mapreduce.shardedfinish": {skip: isAnInternalCommand},
+ mergeChunks: {skip: isPrimaryOnly},
+ moveChunk: {skip: isPrimaryOnly},
+ parallelCollectionScan: {
+ command: {parallelCollectionScan: collName, numCursors: 1},
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NotMasterOrSecondary
+ },
+ ping: {skip: isNotAUserDataRead},
+ planCacheClear: {skip: isNotAUserDataRead},
+ planCacheClearFilters: {skip: isNotAUserDataRead},
+ planCacheListFilters: {skip: isNotAUserDataRead},
+ planCacheListPlans: {skip: isNotAUserDataRead},
+ planCacheListQueryShapes: {skip: isNotAUserDataRead},
+ planCacheSetFilter: {skip: isNotAUserDataRead},
+ prepareTransaction: {skip: isPrimaryOnly},
+ profile: {skip: isPrimaryOnly},
+ reapLogicalSessionCacheNow: {skip: isNotAUserDataRead},
+ refreshLogicalSessionCacheNow: {skip: isNotAUserDataRead},
+ refreshSessions: {skip: isNotAUserDataRead},
+ refreshSessionsInternal: {skip: isNotAUserDataRead},
+ reIndex: {skip: isNotAUserDataRead},
+ renameCollection: {skip: isPrimaryOnly},
+ repairCursor: {skip: isNotAUserDataRead},
+ repairDatabase: {skip: isNotAUserDataRead},
+ replSetAbortPrimaryCatchUp: {skip: isNotAUserDataRead},
+ replSetElect: {skip: isNotAUserDataRead},
+ replSetFreeze: {skip: isNotAUserDataRead},
+ replSetFresh: {skip: isNotAUserDataRead},
+ replSetGetConfig: {skip: isNotAUserDataRead},
+ replSetGetRBID: {skip: isNotAUserDataRead},
+ replSetGetStatus: {skip: isNotAUserDataRead},
+ replSetHeartbeat: {skip: isNotAUserDataRead},
+ replSetInitiate: {skip: isNotAUserDataRead},
+ replSetMaintenance: {skip: isNotAUserDataRead},
+ replSetReconfig: {skip: isNotAUserDataRead},
+ replSetRequestVotes: {skip: isNotAUserDataRead},
+ replSetStepDown: {skip: isNotAUserDataRead},
+ replSetStepUp: {skip: isNotAUserDataRead},
+ replSetSyncFrom: {skip: isNotAUserDataRead},
+ replSetTest: {skip: isNotAUserDataRead},
+ replSetUpdatePosition: {skip: isNotAUserDataRead},
+ replSetResizeOplog: {skip: isNotAUserDataRead},
+ resetError: {skip: isNotAUserDataRead},
+ resync: {skip: isNotAUserDataRead},
+ revokePrivilegesFromRole: {skip: isPrimaryOnly},
+ revokeRolesFromRole: {skip: isPrimaryOnly},
+ revokeRolesFromUser: {skip: isPrimaryOnly},
+ rolesInfo: {skip: isPrimaryOnly},
+ saslContinue: {skip: isPrimaryOnly},
+ saslStart: {skip: isPrimaryOnly},
+ serverStatus: {skip: isNotAUserDataRead},
+ setCommittedSnapshot: {skip: isNotAUserDataRead},
+ setDefaultRWConcern: {skip: isPrimaryOnly},
+ setIndexCommitQuorum: {skip: isPrimaryOnly},
+ setFeatureCompatibilityVersion: {skip: isPrimaryOnly},
+ setFreeMonitoring: {skip: isPrimaryOnly},
+ setParameter: {skip: isNotAUserDataRead},
+ setShardVersion: {skip: isNotAUserDataRead},
+ shardConnPoolStats: {skip: isNotAUserDataRead},
+ shardingState: {skip: isNotAUserDataRead},
+ shutdown: {skip: isNotAUserDataRead},
+ sleep: {skip: isNotAUserDataRead},
+ splitChunk: {skip: isPrimaryOnly},
+ splitVector: {skip: isPrimaryOnly},
+ stageDebug: {skip: isPrimaryOnly},
+ startRecordingTraffic: {skip: isNotAUserDataRead},
+ startSession: {skip: isNotAUserDataRead},
+ stopRecordingTraffic: {skip: isNotAUserDataRead},
+ top: {skip: isNotAUserDataRead},
+ touch: {skip: isNotAUserDataRead},
+ unsetSharding: {skip: isNotAUserDataRead},
+ update: {skip: isPrimaryOnly},
+ updateRole: {skip: isPrimaryOnly},
+ updateUser: {skip: isPrimaryOnly},
+ usersInfo: {skip: isPrimaryOnly},
+ validate: {skip: isNotAUserDataRead},
+ voteCommitIndexBuild: {skip: isNotAUserDataRead},
+ waitForFailPoint: {skip: isNotAUserDataRead},
+ waitForOngoingChunkSplits: {skip: isNotAUserDataRead},
+ whatsmysni: {skip: isNotAUserDataRead},
+ whatsmyuri: {skip: isNotAUserDataRead}
+ };
+
+ /**
+ * Helper function for failing commands or writes that checks the result 'res' of either.
+ * If 'code' is null we only check for failure, otherwise we confirm error code matches as
+ * well. On assert 'msg' is printed.
+ */
+ let assertCommandOrWriteFailed = function(res, code, msg) {
+ if (res.writeErrors !== undefined) {
+ assert.neq(0, res.writeErrors.length, msg);
+ } else if (res.code !== null) {
+ assert.commandFailedWithCode(res, code, msg);
+ } else {
+ assert.commandFailed(res, msg);
+ }
+ };
+
+ // Set up a two-node replica set and put the secondary into RECOVERING state.
+ const rst = new ReplSetTest({name: name, nodes: [{}, {rsConfig: {priority: 0}}]});
+ rst.startSet();
+ rst.initiate();
+
+ const primary = rst.getPrimary();
+ const primaryDb = primary.getDB(dbName);
+ assert.writeOK(primaryDb.getCollection(collName).insert(
+ {a: 42, loc: {type: "Point", coordinates: [1, 1]}}));
+ assert.writeOK(primaryDb.getCollection(collName).insert({b: 4242, pos: {long: 1, lat: 1}}));
+ assert.commandWorked(primaryDb.getCollection(collName).createIndex({loc: "2dsphere"}));
+ assert.commandWorked(primaryDb.getCollection(collName).createIndex(
+ {pos: "geoHaystack", other: 1}, {bucketSize: 1}));
+ rst.awaitReplication();
+
+ const secondary = rst.getSecondary();
+ const secondaryDb = secondary.getDB(dbName);
+
+ // This will lock the node into RECOVERING state until we turn it off.
+ assert.commandWorked(secondary.adminCommand({replSetMaintenance: 1}));
+
+ // Run all tests against the RECOVERING node.
+ AllCommandsTest.testAllCommands(secondary, allCommands, function(test) {
+ const testDb = secondaryDb.getSiblingDB(dbName);
+ let cmdDb = testDb;
+
+ if (test.isAdminCommand) {
+ cmdDb = testDb.getSiblingDB("admin");
+ }
+
+ if (test.expectFailure) {
+ const expectedErrorCode = test.expectedErrorCode;
+ assertCommandOrWriteFailed(
+ cmdDb.runCommand(test.command), expectedErrorCode, () => tojson(test.command));
+ } else {
+ assert.commandWorked(cmdDb.runCommand(test.command), () => tojson(test.command));
+ }
+ });
+
+ // Turn off maintenance mode and stop the test.
+ assert.commandWorked(secondary.adminCommand({replSetMaintenance: 0}));
+ rst.stopSet();
+})(); \ No newline at end of file
diff --git a/jstests/replsets/maintenance2.js b/jstests/replsets/maintenance2.js
index c5e6d9c07e6..0decf05bff3 100644
--- a/jstests/replsets/maintenance2.js
+++ b/jstests/replsets/maintenance2.js
@@ -34,7 +34,7 @@
slaves.forEach(function(slave) {
// put slave into maintenance (recovery) mode
- slave.getDB("foo").adminCommand({replSetMaintenance: 1});
+ assert.commandWorked(slave.getDB("foo").adminCommand({replSetMaintenance: 1}));
var stats = slave.getDB("foo").adminCommand({replSetGetStatus: 1});
assert.eq(stats.myState, 3, "Slave should be in recovering state.");
@@ -54,6 +54,9 @@
print("count should fail in recovering state...");
slave.slaveOk = true;
assert.commandFailed(slave.getDB("foo").runCommand({count: "foo"}));
+
+ // unset maintenance mode when done
+ assert.commandWorked(slave.getDB("foo").adminCommand({replSetMaintenance: 0}));
});
// Shut down the set and finish the test.
diff --git a/jstests/replsets/maintenance_non-blocking.js b/jstests/replsets/maintenance_non-blocking.js
index 5581ffe3546..aa57be0cf64 100644
--- a/jstests/replsets/maintenance_non-blocking.js
+++ b/jstests/replsets/maintenance_non-blocking.js
@@ -41,6 +41,9 @@ doTest = function() {
print("******* fsyncUnlock'n secondary ************* ");
sDB.fsyncUnlock();
+
+ print("******* unset replSetMaintenance on secondary ************* ");
+ assert.commandWorked(sDB.adminCommand({replSetMaintenance: 0}));
replTest.stopSet();
};