diff options
author | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2016-07-14 22:29:31 -0400 |
---|---|---|
committer | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2016-07-17 12:08:30 -0400 |
commit | ff441b6607acaa551d8508af71b84a657ea66161 (patch) | |
tree | baa597cf0223308f13c0df0bb9f14e8d75bb9e21 /src | |
parent | c4f55991f8be792f95d58ece57fca61e6890befd (diff) | |
download | mongo-ff441b6607acaa551d8508af71b84a657ea66161.tar.gz |
SERVER-25064 Make mongos upgrade w:1 to w:majority for auth writes
Also extend the auth tests to validate that both variants work.
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/write_concern_options.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/write_concern_options.h | 1 | ||||
-rw-r--r-- | src/mongo/s/catalog/replset/sharding_catalog_client_impl.cpp | 49 | ||||
-rw-r--r-- | src/mongo/s/catalog/replset/sharding_catalog_test.cpp | 78 |
4 files changed, 87 insertions, 45 deletions
diff --git a/src/mongo/db/write_concern_options.cpp b/src/mongo/db/write_concern_options.cpp index 028d03d4635..dd9694dcc61 100644 --- a/src/mongo/db/write_concern_options.cpp +++ b/src/mongo/db/write_concern_options.cpp @@ -62,6 +62,7 @@ constexpr StringData kWElectionIdFieldName = "wElectionId"_sd; const int WriteConcernOptions::kNoTimeout(0); const int WriteConcernOptions::kNoWaiting(-1); +const StringData WriteConcernOptions::kWriteConcernField = "writeConcern"_sd; const char WriteConcernOptions::kMajority[] = "majority"; const BSONObj WriteConcernOptions::Default = BSONObj(); @@ -159,7 +160,8 @@ StatusWith<WriteConcernOptions> WriteConcernOptions::extractWCFromCommand( } BSONElement writeConcernElement; - Status wcStatus = bsonExtractTypedField(cmdObj, "writeConcern", Object, &writeConcernElement); + Status wcStatus = + bsonExtractTypedField(cmdObj, kWriteConcernField, Object, &writeConcernElement); if (!wcStatus.isOK()) { if (wcStatus == ErrorCodes::NoSuchKey) { // Return default write concern if no write concern is given. diff --git a/src/mongo/db/write_concern_options.h b/src/mongo/db/write_concern_options.h index 06ddfbc56a0..85f9e0e0390 100644 --- a/src/mongo/db/write_concern_options.h +++ b/src/mongo/db/write_concern_options.h @@ -47,6 +47,7 @@ public: static const BSONObj Unacknowledged; static const BSONObj Majority; + static const StringData kWriteConcernField; static const char kMajority[]; // = "majority" WriteConcernOptions() { diff --git a/src/mongo/s/catalog/replset/sharding_catalog_client_impl.cpp b/src/mongo/s/catalog/replset/sharding_catalog_client_impl.cpp index 0c9e47d976e..57d701a82a6 100644 --- a/src/mongo/s/catalog/replset/sharding_catalog_client_impl.cpp +++ b/src/mongo/s/catalog/replset/sharding_catalog_client_impl.cpp @@ -96,8 +96,6 @@ using str::stream; namespace { -const char kWriteConcernField[] = "writeConcern"; - const ReadPreferenceSetting kConfigReadSelector(ReadPreference::Nearest, TagSet{}); const ReadPreferenceSetting kConfigPrimaryPreferredSelector(ReadPreference::PrimaryPreferred, TagSet{}); @@ -767,12 +765,13 @@ Status ShardingCatalogClientImpl::dropCollection(OperationContext* txn, const Na return {ErrorCodes::ShardNotFound, str::stream() << "shard " << shardEntry.getName() << " not found"}; } - auto dropResult = shard->runCommand( - txn, - ReadPreferenceSetting{ReadPreference::PrimaryOnly}, - ns.db().toString(), - BSON("drop" << ns.coll() << "writeConcern" << txn->getWriteConcern().toBSON()), - Shard::RetryPolicy::kIdempotent); + auto dropResult = + shard->runCommand(txn, + ReadPreferenceSetting{ReadPreference::PrimaryOnly}, + ns.db().toString(), + BSON("drop" << ns.coll() << WriteConcernOptions::kWriteConcernField + << txn->getWriteConcern().toBSON()), + Shard::RetryPolicy::kIdempotent); if (!dropResult.isOK()) { return Status(dropResult.getStatus().code(), @@ -1134,24 +1133,26 @@ bool ShardingCatalogClientImpl::runUserManagementWriteCommand(OperationContext* // convert w:1 or no write concern to w:majority before sending. WriteConcernOptions writeConcern; writeConcern.reset(); - const char* writeConcernFieldName = "writeConcern"; - BSONElement writeConcernElement = cmdObj[writeConcernFieldName]; + + BSONElement writeConcernElement = cmdObj[WriteConcernOptions::kWriteConcernField]; bool initialCmdHadWriteConcern = !writeConcernElement.eoo(); if (initialCmdHadWriteConcern) { Status status = writeConcern.parse(writeConcernElement.Obj()); if (!status.isOK()) { return Command::appendCommandStatus(*result, status); } - if (!writeConcern.validForConfigServers()) { + + if (!(writeConcern.wNumNodes == 1 || + writeConcern.wMode == WriteConcernOptions::kMajority)) { return Command::appendCommandStatus( *result, - Status(ErrorCodes::InvalidOptions, - str::stream() - << "Invalid replication write concern. Writes to config server " - "replica sets must use w:'majority', got: " - << writeConcern.toBSON())); + {ErrorCodes::InvalidOptions, + str::stream() << "Invalid replication write concern. User management write " + "commands may only use w:1 or w:'majority', got: " + << writeConcern.toBSON()}); } } + writeConcern.wMode = WriteConcernOptions::kMajority; writeConcern.wNumNodes = 0; @@ -1162,13 +1163,13 @@ bool ShardingCatalogClientImpl::runUserManagementWriteCommand(OperationContext* BSONObjIterator cmdObjIter(cmdObj); while (cmdObjIter.more()) { BSONElement e = cmdObjIter.next(); - if (str::equals(e.fieldName(), writeConcernFieldName)) { + if (WriteConcernOptions::kWriteConcernField == e.fieldName()) { continue; } modifiedCmd.append(e); } } - modifiedCmd.append(writeConcernFieldName, writeConcern.toBSON()); + modifiedCmd.append(WriteConcernOptions::kWriteConcernField, writeConcern.toBSON()); cmdToRun = modifiedCmd.obj(); } @@ -1230,9 +1231,9 @@ Status ShardingCatalogClientImpl::applyChunkOpsDeprecated(OperationContext* txn, const BSONArray& preCondition, const std::string& nss, const ChunkVersion& lastChunkVersion) { - BSONObj cmd = - BSON("applyOps" << updateOps << "preCondition" << preCondition << kWriteConcernField - << ShardingCatalogClient::kMajorityWriteConcern.toBSON()); + BSONObj cmd = BSON("applyOps" << updateOps << "preCondition" << preCondition + << WriteConcernOptions::kWriteConcernField + << ShardingCatalogClient::kMajorityWriteConcern.toBSON()); auto response = Grid::get(txn)->shardRegistry()->getConfigShard()->runCommand( txn, @@ -1511,9 +1512,9 @@ Status ShardingCatalogClientImpl::_checkDbDoesNotExist(OperationContext* txn, Status ShardingCatalogClientImpl::_createCappedConfigCollection(OperationContext* txn, StringData collName, int cappedSize) { - BSONObj createCmd = - BSON("create" << collName << "capped" << true << "size" << cappedSize << "writeConcern" - << ShardingCatalogClient::kMajorityWriteConcern.toBSON()); + BSONObj createCmd = BSON("create" << collName << "capped" << true << "size" << cappedSize + << WriteConcernOptions::kWriteConcernField + << ShardingCatalogClient::kMajorityWriteConcern.toBSON()); auto result = Grid::get(txn)->shardRegistry()->getConfigShard()->runCommand( txn, diff --git a/src/mongo/s/catalog/replset/sharding_catalog_test.cpp b/src/mongo/s/catalog/replset/sharding_catalog_test.cpp index 5ecf1899c60..e4d06494d1b 100644 --- a/src/mongo/s/catalog/replset/sharding_catalog_test.cpp +++ b/src/mongo/s/catalog/replset/sharding_catalog_test.cpp @@ -679,27 +679,9 @@ TEST_F(ShardingCatalogClientTest, RunUserManagementWriteCommandSuccess) { future.timed_get(kFutureTimeout); } -TEST_F(ShardingCatalogClientTest, RunUserManagementWriteCommandWithInvalidWriteConcernSingleNode) { - // Tests that if you send a non-majority write concern it will not be accepted - BSONObjBuilder responseBuilder; - bool ok = - catalogClient()->runUserManagementWriteCommand(operationContext(), - "dropUser", - "test", - BSON("dropUser" - << "test" - << "writeConcern" - << BSON("w" << 1 << "wtimeout" << 30)), - &responseBuilder); - ASSERT_FALSE(ok); - - Status commandStatus = getStatusFromCommandResult(responseBuilder.obj()); - ASSERT_EQUALS(ErrorCodes::InvalidOptions, commandStatus); - ASSERT_STRING_CONTAINS(commandStatus.reason(), "Invalid replication write concern"); -} +TEST_F(ShardingCatalogClientTest, RunUserManagementWriteCommandInvalidWriteConcern) { + configTargeter()->setFindHostReturnValue(HostAndPort("TestHost1")); -TEST_F(ShardingCatalogClientTest, RunUserManagementWriteCommandInvalidWriteConcernTwoNodes) { - // Tests that if you send a non-majority write concern it will not be accepted BSONObjBuilder responseBuilder; bool ok = catalogClient()->runUserManagementWriteCommand(operationContext(), "dropUser", @@ -716,6 +698,62 @@ TEST_F(ShardingCatalogClientTest, RunUserManagementWriteCommandInvalidWriteConce ASSERT_STRING_CONTAINS(commandStatus.reason(), "Invalid replication write concern"); } +TEST_F(ShardingCatalogClientTest, RunUserManagementWriteCommandRewriteWriteConcern) { + // Tests that if you send a w:1 write concern it gets replaced with w:majority + configTargeter()->setFindHostReturnValue(HostAndPort("TestHost1")); + + distLock()->expectLock( + [](StringData name, + StringData whyMessage, + Milliseconds waitFor, + Milliseconds lockTryInterval) { + ASSERT_EQUALS("authorizationData", name); + ASSERT_EQUALS("dropUser", whyMessage); + }, + Status::OK()); + + auto future = launchAsync([this] { + BSONObjBuilder responseBuilder; + bool ok = catalogClient()->runUserManagementWriteCommand(operationContext(), + "dropUser", + "test", + BSON("dropUser" + << "test" + << "writeConcern" + << BSON("w" << 1 << "wtimeout" + << 30)), + &responseBuilder); + ASSERT_FALSE(ok); + + Status commandStatus = getStatusFromCommandResult(responseBuilder.obj()); + ASSERT_EQUALS(ErrorCodes::UserNotFound, commandStatus); + }); + + onCommand([](const RemoteCommandRequest& request) { + ASSERT_EQUALS("test", request.dbname); + ASSERT_EQUALS(BSON("dropUser" + << "test" + << "writeConcern" + << BSON("w" + << "majority" + << "wtimeout" + << 30) + << "maxTimeMS" + << 30000), + request.cmdObj); + + ASSERT_EQUALS(BSON(rpc::kReplSetMetadataFieldName << 1), request.metadata); + + BSONObjBuilder responseBuilder; + Command::appendCommandStatus(responseBuilder, + Status(ErrorCodes::UserNotFound, "User test@test not found")); + return responseBuilder.obj(); + }); + + // Now wait for the runUserManagementWriteCommand call to return + future.timed_get(kFutureTimeout); +} + TEST_F(ShardingCatalogClientTest, RunUserManagementWriteCommandNotMaster) { configTargeter()->setFindHostReturnValue(HostAndPort("TestHost1")); |