diff options
author | Matthew Saltz <matthew.saltz@mongodb.com> | 2018-06-07 14:35:58 -0400 |
---|---|---|
committer | Matthew Saltz <matthew.saltz@mongodb.com> | 2018-06-13 17:02:08 -0400 |
commit | 09919208ae16cd44f3ca3e98220e1cc322419a8a (patch) | |
tree | e0830b8d56407a2131f0b5e09bf489ea42d848e7 | |
parent | 85477a157ab217912eec55422a9d7b2d0e7b0d47 (diff) | |
download | mongo-09919208ae16cd44f3ca3e98220e1cc322419a8a.tar.gz |
SERVER-35486 Create _addShard command on shard to drive shard initialization
22 files changed, 534 insertions, 322 deletions
diff --git a/jstests/auth/lib/commands_lib.js b/jstests/auth/lib/commands_lib.js index 5d057a65c2e..9a9b724e015 100644 --- a/jstests/auth/lib/commands_lib.js +++ b/jstests/auth/lib/commands_lib.js @@ -202,6 +202,28 @@ var authCommandsLib = { ] }, { + testname: "_addShard", + command: { + _addShard: 1, + shardIdentity: { + shardName: "shard0000", + clusterId: ObjectId('5b2031806195dffd744258ee'), + configsvrConnectionString: "foobarbaz/host:20022,host:20023,host:20024" + } + }, + skipSharded: true, // Command doesn't exist on mongos + testcases: [ + { + runOnDb: adminDbName, + roles: {__system: 1}, + privileges: [{resource: {cluster: true}, actions: ["internal"]}], + expectFail: true + }, + {runOnDb: firstDbName, roles: {}}, + {runOnDb: secondDbName, roles: {}} + ] + }, + { testname: "addShard", command: {addShard: "x"}, skipUnlessSharded: true, diff --git a/jstests/core/views/views_all_commands.js b/jstests/core/views/views_all_commands.js index a3b175f5b3a..de5c5dd1676 100644 --- a/jstests/core/views/views_all_commands.js +++ b/jstests/core/views/views_all_commands.js @@ -66,6 +66,7 @@ const isUnrelated = "is unrelated"; let viewsCommandTests = { + _addShard: {skip: isAnInternalCommand}, _cloneCatalogData: {skip: isAnInternalCommand}, _configsvrAddShard: {skip: isAnInternalCommand}, _configsvrAddShardToZone: {skip: isAnInternalCommand}, diff --git a/jstests/sharding/safe_secondary_reads_drop_recreate.js b/jstests/sharding/safe_secondary_reads_drop_recreate.js index b687586816b..d8f4f7fe8d2 100644 --- a/jstests/sharding/safe_secondary_reads_drop_recreate.js +++ b/jstests/sharding/safe_secondary_reads_drop_recreate.js @@ -36,6 +36,7 @@ }; let testCases = { + _addShard: {skip: "primary only"}, _cloneCatalogData: {skip: "primary only"}, _configsvrAddShard: {skip: "primary only"}, _configsvrAddShardToZone: {skip: "primary only"}, diff --git a/jstests/sharding/safe_secondary_reads_single_migration_suspend_range_deletion.js b/jstests/sharding/safe_secondary_reads_single_migration_suspend_range_deletion.js index e51585e26c0..13605561e74 100644 --- a/jstests/sharding/safe_secondary_reads_single_migration_suspend_range_deletion.js +++ b/jstests/sharding/safe_secondary_reads_single_migration_suspend_range_deletion.js @@ -43,6 +43,7 @@ }; let testCases = { + _addShard: {skip: "primary only"}, _cloneCatalogData: {skip: "primary only"}, _configsvrAddShard: {skip: "primary only"}, _configsvrAddShardToZone: {skip: "primary only"}, diff --git a/jstests/sharding/safe_secondary_reads_single_migration_waitForDelete.js b/jstests/sharding/safe_secondary_reads_single_migration_waitForDelete.js index 9611135a6ac..85f236dbae1 100644 --- a/jstests/sharding/safe_secondary_reads_single_migration_waitForDelete.js +++ b/jstests/sharding/safe_secondary_reads_single_migration_waitForDelete.js @@ -36,6 +36,7 @@ }; let testCases = { + _addShard: {skip: "primary only"}, _cloneCatalogData: {skip: "primary only"}, _configsvrAddShard: {skip: "primary only"}, _configsvrAddShardToZone: {skip: "primary only"}, diff --git a/src/mongo/client/connection_string.cpp b/src/mongo/client/connection_string.cpp index 5e8a5f4e0bb..b4d4eed0a00 100644 --- a/src/mongo/client/connection_string.cpp +++ b/src/mongo/client/connection_string.cpp @@ -217,6 +217,10 @@ StatusWith<ConnectionString> ConnectionString::parse(const std::string& url) { return Status(ErrorCodes::FailedToParse, str::stream() << "invalid url [" << url << "]"); } +ConnectionString ConnectionString::deserialize(StringData url) { + return uassertStatusOK(parse(url.toString())); +} + std::string ConnectionString::typeToString(ConnectionType type) { switch (type) { case INVALID: diff --git a/src/mongo/client/connection_string.h b/src/mongo/client/connection_string.h index 0467a77dce3..3c8a327a781 100644 --- a/src/mongo/client/connection_string.h +++ b/src/mongo/client/connection_string.h @@ -127,6 +127,12 @@ public: static StatusWith<ConnectionString> parse(const std::string& url); + /** + * Deserialize a ConnectionString object from a string. Used by the IDL parser for the + * connectionstring type. Essentially just a throwing wrapper around ConnectionString::parse. + */ + static ConnectionString deserialize(StringData url); + static std::string typeToString(ConnectionType type); // diff --git a/src/mongo/db/s/SConscript b/src/mongo/db/s/SConscript index 2216e68abae..cb57c8b8456 100644 --- a/src/mongo/db/s/SConscript +++ b/src/mongo/db/s/SConscript @@ -95,6 +95,7 @@ env.Library( env.Library( target='type_shard_identity', source=[ + env.Idlc('add_shard_cmd.idl')[0], 'type_shard_identity.cpp', ], LIBDEPS=[ @@ -184,6 +185,7 @@ env.CppUnitTest( env.Library( target='sharding_catalog_manager', source=[ + 'add_shard_util.cpp', 'config/sharding_catalog_manager_chunk_operations.cpp', 'config/sharding_catalog_manager_collection_operations.cpp', 'config/sharding_catalog_manager.cpp', @@ -214,6 +216,7 @@ env.Library( env.Library( target='commands_db_s', source=[ + 'add_shard_cmd.cpp', 'check_sharding_index_command.cpp', 'cleanup_orphaned_cmd.cpp', 'clone_catalog_data_command.cpp', diff --git a/src/mongo/db/s/add_shard_cmd.cpp b/src/mongo/db/s/add_shard_cmd.cpp new file mode 100644 index 00000000000..0f06a488cd8 --- /dev/null +++ b/src/mongo/db/s/add_shard_cmd.cpp @@ -0,0 +1,103 @@ +/** + * Copyright (C) 2018 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects + * for all of the code used other than as permitted herein. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you do not + * wish to do so, delete this exception statement from your version. If you + * delete this exception statement from all source files in the program, + * then also delete it in the license file. + */ + +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kSharding + +#include "mongo/platform/basic.h" + +#include "mongo/db/audit.h" +#include "mongo/db/auth/action_type.h" +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/auth/privilege.h" +#include "mongo/db/commands.h" +#include "mongo/db/dbdirectclient.h" +#include "mongo/db/s/add_shard_cmd_gen.h" +#include "mongo/db/s/add_shard_util.h" +#include "mongo/db/s/config/sharding_catalog_manager.h" +#include "mongo/rpc/get_status_from_command_result.h" +#include "mongo/util/log.h" + +namespace mongo { +namespace { +/** + * Internal sharding command run on mongod to initialize itself as a shard in the cluster. + */ +class AddShardCommand : public TypedCommand<AddShardCommand> { +public: + using Request = AddShard; + class Invocation final : public InvocationBase { + public: + using InvocationBase::InvocationBase; + + void typedRun(OperationContext* opCtx) { + auto addShardCmd = request(); + auto shardIdUpsertCmd = + add_shard_util::createShardIdentityUpsertForAddShard(addShardCmd); + DBDirectClient localClient(opCtx); + BSONObj res; + + localClient.runCommand(NamespaceString::kAdminDb.toString(), shardIdUpsertCmd, res); + uassertStatusOK(getStatusFromCommandResult(res)); + } + + private: + bool supportsWriteConcern() const override { + return true; + } + + // The command parameter happens to be string so it's historically been interpreted + // by parseNs as a collection. Continuing to do so here for unexamined compatibility. + NamespaceString ns() const override { + return NamespaceString(request().getDbName(), ""); + } + + void doCheckAuthorization(OperationContext* opCtx) const override { + uassert(ErrorCodes::Unauthorized, + "Unauthorized", + AuthorizationSession::get(opCtx->getClient()) + ->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), + ActionType::internal)); + } + }; + + std::string help() const override { + return "Internal command, which is exported by shards. Do not call " + "directly. Adds a new shard to a cluster."; + } + + AllowedOnSecondary secondaryAllowed(ServiceContext*) const override { + return AllowedOnSecondary::kNever; + } + + bool adminOnly() const override { + return true; + } +} addShardCmd; + +} // namespace +} // namespace mongo diff --git a/src/mongo/db/s/add_shard_cmd.idl b/src/mongo/db/s/add_shard_cmd.idl new file mode 100644 index 00000000000..fe6af62c7e2 --- /dev/null +++ b/src/mongo/db/s/add_shard_cmd.idl @@ -0,0 +1,53 @@ +# Copyright (C) 2018 MongoDB Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +global: + cpp_namespace: "mongo" + cpp_includes: + - "mongo/client/connection_string.h" + +imports: + - "mongo/idl/basic_types.idl" + +types: + connectionstring: + bson_serialization_type: string + description: "A MongoDB ConnectionString" + cpp_type: "mongo::ConnectionString" + serializer: mongo::ConnectionString::toString + deserializer: mongo::ConnectionString::deserialize + +structs: + ShardIdentity: + description: "Contains all the information needed to identify a shard and lookup the shard identity document from storage" + fields: + shardName: + description: "The name of the shard" + type: string + clusterId: + description: "The unique identifier of the cluster" + type: objectid + configsvrConnectionString: + description: "Connection string to the config server" + type: connectionstring + +commands: + _addShard: + cpp_name: AddShard + description: "_addShard Command" + namespace: ignored + fields: + shardIdentity: + description: "Identity metadata for the new shard" + type: ShardIdentity diff --git a/src/mongo/db/s/add_shard_util.cpp b/src/mongo/db/s/add_shard_util.cpp new file mode 100644 index 00000000000..a71016243b8 --- /dev/null +++ b/src/mongo/db/s/add_shard_util.cpp @@ -0,0 +1,80 @@ +/** + * Copyright (C) 2018 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects + * for all of the code used other than as permitted herein. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you do not + * wish to do so, delete this exception statement from your version. If you + * delete this exception statement from all source files in the program, + * then also delete it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/db/s/add_shard_util.h" + +#include "mongo/bson/bsonobj.h" +#include "mongo/db/operation_context.h" +#include "mongo/db/ops/write_ops.h" +#include "mongo/db/repl/repl_set_config.h" +#include "mongo/db/repl/replication_coordinator.h" +#include "mongo/db/s/add_shard_cmd_gen.h" +#include "mongo/s/catalog/sharding_catalog_client.h" +#include "mongo/s/cluster_identity_loader.h" +#include "mongo/s/shard_id.h" +#include "mongo/s/write_ops/batched_command_request.h" + +namespace mongo { +namespace add_shard_util { + +AddShard createAddShardCmd(OperationContext* opCtx, const ShardId& shardName) { + AddShard addShardCmd; + addShardCmd.setDbName(NamespaceString::kAdminDb); + + ShardIdentity shardIdentity; + shardIdentity.setShardName(shardName.toString()); + shardIdentity.setClusterId(ClusterIdentityLoader::get(opCtx)->getClusterId()); + shardIdentity.setConfigsvrConnectionString( + repl::ReplicationCoordinator::get(opCtx)->getConfig().getConnectionString()); + + addShardCmd.setShardIdentity(shardIdentity); + return addShardCmd; +} + +BSONObj createShardIdentityUpsertForAddShard(const AddShard& addShardCmd) { + BatchedCommandRequest request([&] { + write_ops::Update updateOp(NamespaceString::kServerConfigurationNamespace); + updateOp.setUpdates({[&] { + write_ops::UpdateOpEntry entry; + entry.setQ(BSON("_id" << kShardIdentityDocumentId)); + entry.setU(addShardCmd.getShardIdentity().toBSON()); + entry.setUpsert(true); + return entry; + }()}); + + return updateOp; + }()); + request.setWriteConcern(ShardingCatalogClient::kMajorityWriteConcern.toBSON()); + + return request.toBSON(); +} + +} // namespace mongo +} // namespace add_shard_util diff --git a/src/mongo/db/s/add_shard_util.h b/src/mongo/db/s/add_shard_util.h new file mode 100644 index 00000000000..9a525e44a58 --- /dev/null +++ b/src/mongo/db/s/add_shard_util.h @@ -0,0 +1,63 @@ +/** + * Copyright (C) 2018 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects + * for all of the code used other than as permitted herein. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you do not + * wish to do so, delete this exception statement from your version. If you + * delete this exception statement from all source files in the program, + * then also delete it in the license file. + */ + +#pragma once + +#include <string> + +#include "mongo/base/string_data.h" + +namespace mongo { +class AddShard; +class BSONObj; +class OperationContext; +class ShardId; + +// Contains a collection of utility functions relating to the addShard command +namespace add_shard_util { + +/* + * The _id value for shard identity documents + */ +constexpr StringData kShardIdentityDocumentId = "shardIdentity"_sd; + +/** + * Creates an AddShard command object that's sent from the config server to + * a mongod to instruct it to initialize itself as a shard in the cluster. + */ +AddShard createAddShardCmd(OperationContext* opCtx, const ShardId& shardName); + +/** + * Returns a BSON representation of an update request that can be used to insert a shardIdentity + * doc into the shard with the given shardName (or update the shard's existing shardIdentity + * doc's configsvrConnString if the _id, shardName, and clusterId do not conflict). + */ +BSONObj createShardIdentityUpsertForAddShard(const AddShard& addShardCmd); + +} // namespace mongo +} // namespace add_shard_util diff --git a/src/mongo/db/s/collection_sharding_state_test.cpp b/src/mongo/db/s/collection_sharding_state_test.cpp index d416840861c..401edadd7a0 100644 --- a/src/mongo/db/s/collection_sharding_state_test.cpp +++ b/src/mongo/db/s/collection_sharding_state_test.cpp @@ -66,19 +66,19 @@ private: TEST_F(CollectionShardingStateTest, GlobalInitGetsCalledAfterWriteCommits) { ShardIdentityType shardIdentity; - shardIdentity.setConfigsvrConnString( + shardIdentity.setConfigsvrConnectionString( ConnectionString(ConnectionString::SET, "a:1,b:2", "config")); shardIdentity.setShardName("a"); shardIdentity.setClusterId(OID::gen()); DBDirectClient client(operationContext()); - client.insert("admin.system.version", shardIdentity.toBSON()); + client.insert("admin.system.version", shardIdentity.toShardIdentityDocument()); ASSERT_EQ(1, getInitCallCount()); } TEST_F(CollectionShardingStateTest, GlobalInitDoesntGetCalledIfWriteAborts) { ShardIdentityType shardIdentity; - shardIdentity.setConfigsvrConnString( + shardIdentity.setConfigsvrConnectionString( ConnectionString(ConnectionString::SET, "a:1,b:2", "config")); shardIdentity.setShardName("a"); shardIdentity.setClusterId(OID::gen()); @@ -95,7 +95,7 @@ TEST_F(CollectionShardingStateTest, GlobalInitDoesntGetCalledIfWriteAborts) { WriteUnitOfWork wuow(operationContext()); ASSERT_OK(autoColl.getCollection()->insertDocument( - operationContext(), shardIdentity.toBSON(), {}, false)); + operationContext(), shardIdentity.toShardIdentityDocument(), {}, false)); ASSERT_EQ(0, getInitCallCount()); } @@ -104,22 +104,21 @@ TEST_F(CollectionShardingStateTest, GlobalInitDoesntGetCalledIfWriteAborts) { TEST_F(CollectionShardingStateTest, GlobalInitDoesntGetsCalledIfNSIsNotForShardIdentity) { ShardIdentityType shardIdentity; - shardIdentity.setConfigsvrConnString( + shardIdentity.setConfigsvrConnectionString( ConnectionString(ConnectionString::SET, "a:1,b:2", "config")); shardIdentity.setShardName("a"); shardIdentity.setClusterId(OID::gen()); DBDirectClient client(operationContext()); - client.insert("admin.user", shardIdentity.toBSON()); + client.insert("admin.user", shardIdentity.toShardIdentityDocument()); ASSERT_EQ(0, getInitCallCount()); } TEST_F(CollectionShardingStateTest, OnInsertOpThrowWithIncompleteShardIdentityDocument) { - ShardIdentityType shardIdentity; - shardIdentity.setShardName("a"); - DBDirectClient client(operationContext()); - client.insert("admin.system.version", shardIdentity.toBSON()); + client.insert( + "admin.system.version", + BSON("_id" << ShardIdentityType::IdName << ShardIdentity::kShardNameFieldName << "a")); ASSERT(!client.getLastError().empty()); } diff --git a/src/mongo/db/s/config/sharding_catalog_manager.h b/src/mongo/db/s/config/sharding_catalog_manager.h index 15edcbd1319..77938d09d82 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager.h +++ b/src/mongo/db/s/config/sharding_catalog_manager.h @@ -342,14 +342,6 @@ public: // /** - * Returns a BSON representation of an update request that can be used to insert a shardIdentity - * doc into the shard for the given shardType (or update the shard's existing shardIdentity - * doc's configsvrConnString if the _id, shardName, and clusterId do not conflict). - */ - BSONObj createShardIdentityUpsertForAddShard(OperationContext* opCtx, - const std::string& shardName); - - /** * Runs the setFeatureCompatibilityVersion command on all shards. */ Status setFeatureCompatibilityVersionOnShards(OperationContext* opCtx, const BSONObj& cmdObj); diff --git a/src/mongo/db/s/config/sharding_catalog_manager_add_shard_test.cpp b/src/mongo/db/s/config/sharding_catalog_manager_add_shard_test.cpp index 1ec6b87f2cc..60bb75312d0 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager_add_shard_test.cpp +++ b/src/mongo/db/s/config/sharding_catalog_manager_add_shard_test.cpp @@ -38,6 +38,8 @@ #include "mongo/db/commands.h" #include "mongo/db/ops/write_ops.h" #include "mongo/db/repl/replication_coordinator_mock.h" +#include "mongo/db/s/add_shard_cmd_gen.h" +#include "mongo/db/s/add_shard_util.h" #include "mongo/db/s/config/sharding_catalog_manager.h" #include "mongo/db/s/type_shard_identity.h" #include "mongo/s/catalog/config_server_version.h" @@ -153,12 +155,12 @@ protected: * Waits for a request for the shardIdentity document to be upserted into a shard from the * config server on addShard. */ - void expectShardIdentityUpsertReturnSuccess(const HostAndPort& expectedHost, - const std::string& expectedShardName) { + void expectAddShardCmdReturnSuccess(const HostAndPort& expectedHost, + const std::string& expectedShardName) { + using namespace add_shard_util; // Create the expected upsert shardIdentity command for this shardType. - auto upsertCmdObj = - ShardingCatalogManager::get(operationContext()) - ->createShardIdentityUpsertForAddShard(operationContext(), expectedShardName); + auto upsertCmdObj = createShardIdentityUpsertForAddShard( + createAddShardCmd(operationContext(), expectedShardName)); const auto opMsgRequest = OpMsgRequest::fromDBAndBody(NamespaceString::kAdminDb, upsertCmdObj); @@ -167,13 +169,13 @@ protected: UpdateOp::parse(opMsgRequest)); } - void expectShardIdentityUpsertReturnFailure(const HostAndPort& expectedHost, - const std::string& expectedShardName, - const Status& statusToReturn) { + void expectAddShardCmdReturnFailure(const HostAndPort& expectedHost, + const std::string& expectedShardName, + const Status& statusToReturn) { + using namespace add_shard_util; // Create the expected upsert shardIdentity command for this shardType. - auto upsertCmdObj = - ShardingCatalogManager::get(operationContext()) - ->createShardIdentityUpsertForAddShard(operationContext(), expectedShardName); + auto upsertCmdObj = createShardIdentityUpsertForAddShard( + createAddShardCmd(operationContext(), expectedShardName)); const auto opMsgRequest = OpMsgRequest::fromDBAndBody(NamespaceString::kAdminDb, upsertCmdObj); @@ -196,8 +198,20 @@ protected: // Check that the db name in the request matches the expected db name. ASSERT_EQUALS(expectedNss.db(), request.dbname); - const auto opMsgRequest = OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); - const auto updateOp = UpdateOp::parse(opMsgRequest); + const auto addShardOpMsgRequest = + OpMsgRequest::fromDBAndBody(request.dbname, request.cmdObj); + + auto addShardCmd = AddShard::parse(IDLParserErrorContext(AddShard::kCommandName), + addShardOpMsgRequest); + + const auto& updateOpField = + add_shard_util::createShardIdentityUpsertForAddShard(addShardCmd); + + const auto updateOpMsgRequest = + OpMsgRequest::fromDBAndBody(request.dbname, updateOpField); + + const auto updateOp = UpdateOp::parse(updateOpMsgRequest); + ASSERT_EQUALS(expectedNss, expectedUpdateOp.getNamespace()); const auto& expectedUpdates = expectedUpdateOp.getUpdates(); @@ -336,23 +350,21 @@ TEST_F(AddShardTest, CreateShardIdentityUpsertForAddShard) { << "ordered" << true << "updates" - << BSON_ARRAY( - BSON("q" << BSON("_id" - << "shardIdentity" - << "shardName" - << shardName - << "clusterId" - << _clusterId) - << "u" - << BSON("$set" << BSON("configsvrConnectionString" - << replicationCoordinator() - ->getConfig() - .getConnectionString() - .toString())) - << "multi" - << false - << "upsert" - << true)) + << BSON_ARRAY(BSON( + "q" + << BSON("_id" + << "shardIdentity") + << "u" + << BSON("shardName" << shardName << "clusterId" << _clusterId + << "configsvrConnectionString" + << replicationCoordinator() + ->getConfig() + .getConnectionString() + .toString()) + << "multi" + << false + << "upsert" + << true)) << "writeConcern" << BSON("w" << "majority" @@ -360,9 +372,9 @@ TEST_F(AddShardTest, CreateShardIdentityUpsertForAddShard) { << 15000) << "allowImplicitCollectionCreation" << true); - ASSERT_BSONOBJ_EQ(expectedBSON, - ShardingCatalogManager::get(operationContext()) - ->createShardIdentityUpsertForAddShard(operationContext(), shardName)); + auto addShardCmd = add_shard_util::createAddShardCmd(operationContext(), shardName); + auto actualBSON = add_shard_util::createShardIdentityUpsertForAddShard(addShardCmd); + ASSERT_BSONOBJ_EQ(expectedBSON, actualBSON); } TEST_F(AddShardTest, StandaloneBasicSuccess) { @@ -416,8 +428,8 @@ TEST_F(AddShardTest, StandaloneBasicSuccess) { expectCollectionDrop(shardTarget, NamespaceString("config", "system.sessions")); - // The shardIdentity doc inserted into the admin.system.version collection on the shard. - expectShardIdentityUpsertReturnSuccess(shardTarget, expectedShardName); + // The shard receives the _addShard command + expectAddShardCmdReturnSuccess(shardTarget, expectedShardName); // The shard receives the setFeatureCompatibilityVersion command. expectSetFeatureCompatibilityVersion( @@ -495,8 +507,8 @@ TEST_F(AddShardTest, StandaloneGenerateName) { expectCollectionDrop(shardTarget, NamespaceString("config", "system.sessions")); - // The shardIdentity doc inserted into the admin.system.version collection on the shard. - expectShardIdentityUpsertReturnSuccess(shardTarget, expectedShardName); + // The shard receives the _addShard command + expectAddShardCmdReturnSuccess(shardTarget, expectedShardName); // The shard receives the setFeatureCompatibilityVersion command. expectSetFeatureCompatibilityVersion( @@ -889,8 +901,8 @@ TEST_F(AddShardTest, SuccessfullyAddReplicaSet) { expectCollectionDrop(shardTarget, NamespaceString("config", "system.sessions")); - // The shardIdentity doc inserted into the admin.system.version collection on the shard. - expectShardIdentityUpsertReturnSuccess(shardTarget, expectedShardName); + // The shard receives the _addShard command + expectAddShardCmdReturnSuccess(shardTarget, expectedShardName); // The shard receives the setFeatureCompatibilityVersion command. expectSetFeatureCompatibilityVersion( @@ -954,8 +966,8 @@ TEST_F(AddShardTest, ReplicaSetExtraHostsDiscovered) { expectCollectionDrop(shardTarget, NamespaceString("config", "system.sessions")); - // The shardIdentity doc inserted into the admin.system.version collection on the shard. - expectShardIdentityUpsertReturnSuccess(shardTarget, expectedShardName); + // The shard receives the _addShard command + expectAddShardCmdReturnSuccess(shardTarget, expectedShardName); // The shard receives the setFeatureCompatibilityVersion command. expectSetFeatureCompatibilityVersion( @@ -1031,8 +1043,8 @@ TEST_F(AddShardTest, AddShardSucceedsEvenIfAddingDBsFromNewShardFails) { expectCollectionDrop(shardTarget, NamespaceString("config", "system.sessions")); - // The shardIdentity doc inserted into the admin.system.version collection on the shard. - expectShardIdentityUpsertReturnSuccess(shardTarget, expectedShardName); + // The shard receives the _addShard command + expectAddShardCmdReturnSuccess(shardTarget, expectedShardName); // The shard receives the setFeatureCompatibilityVersion command. expectSetFeatureCompatibilityVersion( diff --git a/src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp b/src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp index 3bb1bee9a0d..182637837cd 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp +++ b/src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp @@ -53,6 +53,8 @@ #include "mongo/db/repl/repl_client_info.h" #include "mongo/db/repl/repl_set_config.h" #include "mongo/db/repl/replication_coordinator.h" +#include "mongo/db/s/add_shard_cmd_gen.h" +#include "mongo/db/s/add_shard_util.h" #include "mongo/db/s/type_shard_identity.h" #include "mongo/db/wire_version.h" #include "mongo/executor/task_executor.h" @@ -642,21 +644,48 @@ StatusWith<std::string> ShardingCatalogManager::addShard( shardType.setMaxSizeMB(maxSize); } - // Insert a shardIdentity document onto the shard. This also triggers sharding initialization on - // the shard. - LOG(2) << "going to insert shardIdentity document into shard: " << shardType; - auto commandRequest = createShardIdentityUpsertForAddShard(opCtx, shardType.getName()); - auto swCommandResponse = - _runCommandForAddShard(opCtx, targeter.get(), NamespaceString::kAdminDb, commandRequest); - if (!swCommandResponse.isOK()) { - return swCommandResponse.getStatus(); - } - auto commandResponse = std::move(swCommandResponse.getValue()); - BatchedCommandResponse batchResponse; - auto batchResponseStatus = - Shard::CommandResponse::processBatchWriteResponse(commandResponse, &batchResponse); - if (!batchResponseStatus.isOK()) { - return batchResponseStatus; + // Helper function that runs a command on the to-be shard and returns the status + auto runCmdOnNewShard = [this, &opCtx, &targeter](const BSONObj& cmd) -> Status { + auto response = + _runCommandForAddShard(opCtx, targeter.get(), NamespaceString::kAdminDb, cmd); + // Grabs the underlying status from a StatusWith object by taking the first + // non-OK status, if there is one. This is needed due to the semantics of + // _runCommandForAddShard. + auto commandResponse = std::move(response.getValue()); + BatchedCommandResponse batchResponse; + return Shard::CommandResponse::processBatchWriteResponse(commandResponse, &batchResponse); + }; + + // Run _addShard command on the shard, which in turn inserts a shardIdentity document onto the + // shard and triggers initialization + LOG(2) << "going to run _addShard command on shard: " << shardType; + AddShard addShardCmd = add_shard_util::createAddShardCmd(opCtx, shardType.getName()); + BSONObj passthroughFields; // Needed for IDL toBSON method + auto addShardCmdBSON = addShardCmd.toBSON(passthroughFields); + + // Run _addShard command + auto addShardStatus = runCmdOnNewShard(addShardCmdBSON); + + if (!addShardStatus.isOK()) { + // TODO (SERVER-35552): Fix this to use an FCV check instead + // If the _addShard command is not found, that means the mongod for the shard we're adding + // is running on an older version, so we retry instead with the old method of inserting a + // shard identity document directly. + if (addShardStatus == ErrorCodes::CommandNotFound) { + // Insert a shardIdentity document onto the shard. This also triggers sharding + // initialization on the shard. + LOG(2) << AddShard::kCommandName + << " command not found. going to insert shardIdentity document into " + "shard: " + << shardType; + + auto idCommand = add_shard_util::createShardIdentityUpsertForAddShard(addShardCmd); + auto shardUpsertCmdStatus = runCmdOnNewShard(idCommand); + + if (!shardUpsertCmdStatus.isOK()) { + return shardUpsertCmdStatus; + } + } } { @@ -888,33 +917,6 @@ void ShardingCatalogManager::appendConnectionStats(executor::ConnectionPoolStats _executorForAddShard->appendConnectionStats(stats); } -BSONObj ShardingCatalogManager::createShardIdentityUpsertForAddShard(OperationContext* opCtx, - const std::string& shardName) { - BatchedCommandRequest request([&] { - write_ops::Update updateOp(NamespaceString::kServerConfigurationNamespace); - updateOp.setUpdates( - {[&] { - write_ops::UpdateOpEntry entry; - entry.setQ(BSON("_id" - << "shardIdentity" - << ShardIdentityType::shardName(shardName) - << ShardIdentityType::clusterId( - ClusterIdentityLoader::get(opCtx)->getClusterId()))); - entry.setU(BSON("$set" << BSON(ShardIdentityType::configsvrConnString( - repl::ReplicationCoordinator::get(opCtx) - ->getConfig() - .getConnectionString() - .toString())))); - entry.setUpsert(true); - return entry; - }()}); - return updateOp; - }()); - request.setWriteConcern(ShardingCatalogClient::kMajorityWriteConcern.toBSON()); - - return request.toBSON(); -} - // static StatusWith<ShardId> ShardingCatalogManager::_selectShardForNewDatabase( OperationContext* opCtx, ShardRegistry* shardRegistry) { diff --git a/src/mongo/db/s/shard_server_op_observer.cpp b/src/mongo/db/s/shard_server_op_observer.cpp index 51c9fe954c7..9b8ba53ae89 100644 --- a/src/mongo/db/s/shard_server_op_observer.cpp +++ b/src/mongo/db/s/shard_server_op_observer.cpp @@ -211,7 +211,7 @@ void ShardServerOpObserver::onInserts(OperationContext* opCtx, if (auto idElem = insertedDoc["_id"]) { if (idElem.str() == ShardIdentityType::IdName) { auto shardIdentityDoc = - uassertStatusOK(ShardIdentityType::fromBSON(insertedDoc)); + uassertStatusOK(ShardIdentityType::fromShardIdentityDocument(insertedDoc)); uassertStatusOK(shardIdentityDoc.validate()); opCtx->recoveryUnit()->registerChange( new ShardIdentityLogOpHandler(opCtx, std::move(shardIdentityDoc))); diff --git a/src/mongo/db/s/sharding_state.cpp b/src/mongo/db/s/sharding_state.cpp index 3850454d36f..c45ddb1e71d 100644 --- a/src/mongo/db/s/sharding_state.cpp +++ b/src/mongo/db/s/sharding_state.cpp @@ -190,7 +190,7 @@ Status ShardingState::initializeFromShardIdentity(OperationContext* opCtx, stdx::unique_lock<stdx::mutex> lk(_mutex); - auto configSvrConnStr = shardIdentity.getConfigsvrConnString(); + const auto& configSvrConnStr = shardIdentity.getConfigsvrConnectionString(); if (enabled()) { invariant(!_shardName.empty()); @@ -241,7 +241,7 @@ Status ShardingState::initializeFromShardIdentity(OperationContext* opCtx, _initializationStatus = status; _setInitializationState(InitializationState::kError); } - _shardName = shardIdentity.getShardName(); + _shardName = shardIdentity.getShardName().toString(); _clusterId = shardIdentity.getClusterId(); return status; @@ -275,8 +275,8 @@ StatusWith<bool> ShardingState::initializeShardingAwarenessIfNeeded(OperationCon "If started with --shardsvr in queryableBackupMode, a shardIdentity " "document must be provided through --overrideShardIdentity"}; } - auto swOverrideShardIdentity = - ShardIdentityType::fromBSON(serverGlobalParams.overrideShardIdentity); + auto swOverrideShardIdentity = ShardIdentityType::fromShardIdentityDocument( + serverGlobalParams.overrideShardIdentity); if (!swOverrideShardIdentity.isOK()) { return swOverrideShardIdentity.getStatus(); } @@ -345,7 +345,7 @@ StatusWith<bool> ShardingState::initializeShardingAwarenessIfNeeded(OperationCon invariant(!shardIdentityBSON.isEmpty()); - auto swShardIdentity = ShardIdentityType::fromBSON(shardIdentityBSON); + auto swShardIdentity = ShardIdentityType::fromShardIdentityDocument(shardIdentityBSON); if (!swShardIdentity.isOK()) { return swShardIdentity.getStatus(); } diff --git a/src/mongo/db/s/sharding_state_test.cpp b/src/mongo/db/s/sharding_state_test.cpp index 1b29fe5ceae..94346a3e34d 100644 --- a/src/mongo/db/s/sharding_state_test.cpp +++ b/src/mongo/db/s/sharding_state_test.cpp @@ -155,7 +155,7 @@ TEST_F(ShardingStateTest, ValidShardIdentitySucceeds) { Lock::GlobalWrite lk(operationContext()); ShardIdentityType shardIdentity; - shardIdentity.setConfigsvrConnString( + shardIdentity.setConfigsvrConnectionString( ConnectionString(ConnectionString::SET, "a:1,b:2", "config")); shardIdentity.setShardName(kShardName); shardIdentity.setClusterId(OID::gen()); @@ -171,7 +171,7 @@ TEST_F(ShardingStateTest, InitWhilePreviouslyInErrorStateWillStayInErrorState) { Lock::GlobalWrite lk(operationContext()); ShardIdentityType shardIdentity; - shardIdentity.setConfigsvrConnString( + shardIdentity.setConfigsvrConnectionString( ConnectionString(ConnectionString::SET, "a:1,b:2", "config")); shardIdentity.setShardName(kShardName); shardIdentity.setClusterId(OID::gen()); @@ -209,7 +209,7 @@ TEST_F(ShardingStateTest, InitializeAgainWithMatchingShardIdentitySucceeds) { auto clusterID = OID::gen(); ShardIdentityType shardIdentity; - shardIdentity.setConfigsvrConnString( + shardIdentity.setConfigsvrConnectionString( ConnectionString(ConnectionString::SET, "a:1,b:2", "config")); shardIdentity.setShardName(kShardName); shardIdentity.setClusterId(clusterID); @@ -217,7 +217,7 @@ TEST_F(ShardingStateTest, InitializeAgainWithMatchingShardIdentitySucceeds) { ASSERT_OK(shardingState()->initializeFromShardIdentity(operationContext(), shardIdentity)); ShardIdentityType shardIdentity2; - shardIdentity2.setConfigsvrConnString( + shardIdentity2.setConfigsvrConnectionString( ConnectionString(ConnectionString::SET, "a:1,b:2", "config")); shardIdentity2.setShardName(kShardName); shardIdentity2.setClusterId(clusterID); @@ -240,7 +240,7 @@ TEST_F(ShardingStateTest, InitializeAgainWithSameReplSetNameSucceeds) { auto clusterID = OID::gen(); ShardIdentityType shardIdentity; - shardIdentity.setConfigsvrConnString( + shardIdentity.setConfigsvrConnectionString( ConnectionString(ConnectionString::SET, "a:1,b:2", "config")); shardIdentity.setShardName(kShardName); shardIdentity.setClusterId(clusterID); @@ -248,7 +248,7 @@ TEST_F(ShardingStateTest, InitializeAgainWithSameReplSetNameSucceeds) { ASSERT_OK(shardingState()->initializeFromShardIdentity(operationContext(), shardIdentity)); ShardIdentityType shardIdentity2; - shardIdentity2.setConfigsvrConnString( + shardIdentity2.setConfigsvrConnectionString( ConnectionString(ConnectionString::SET, "b:2,c:3", "config")); shardIdentity2.setShardName(kShardName); shardIdentity2.setClusterId(clusterID); @@ -281,10 +281,15 @@ TEST_F(ShardingStateTest, TEST_F(ShardingStateTest, InitializeShardingAwarenessIfNeededReadOnlyAndShardServerAndInvalidOverrideShardIdentity) { storageGlobalParams.readOnly = true; - serverGlobalParams.overrideShardIdentity = BSON("_id" - << "shardIdentity" - << "configsvrConnectionString" - << "invalid"); + serverGlobalParams.overrideShardIdentity = + BSON("_id" + << "shardIdentity" + << ShardIdentity::kShardNameFieldName + << kShardName + << ShardIdentity::kClusterIdFieldName + << OID::gen() + << ShardIdentity::kConfigsvrConnectionStringFieldName + << "invalid"); auto swShardingInitialized = shardingState()->initializeShardingAwarenessIfNeeded(operationContext()); ASSERT_EQUALS(ErrorCodes::UnsupportedFormat, swShardingInitialized.getStatus().code()); @@ -296,12 +301,12 @@ TEST_F(ShardingStateTest, serverGlobalParams.clusterRole = ClusterRole::ShardServer; ShardIdentityType shardIdentity; - shardIdentity.setConfigsvrConnString( + shardIdentity.setConfigsvrConnectionString( ConnectionString(ConnectionString::SET, "a:1,b:2", "config")); shardIdentity.setShardName(kShardName); shardIdentity.setClusterId(OID::gen()); ASSERT_OK(shardIdentity.validate()); - serverGlobalParams.overrideShardIdentity = shardIdentity.toBSON(); + serverGlobalParams.overrideShardIdentity = shardIdentity.toShardIdentityDocument(); auto swShardingInitialized = shardingState()->initializeShardingAwarenessIfNeeded(operationContext()); @@ -343,12 +348,12 @@ TEST_F(ShardingStateTest, serverGlobalParams.clusterRole = ClusterRole::None; ShardIdentityType shardIdentity; - shardIdentity.setConfigsvrConnString( + shardIdentity.setConfigsvrConnectionString( ConnectionString(ConnectionString::SET, "a:1,b:2", "config")); shardIdentity.setShardName(kShardName); shardIdentity.setClusterId(OID::gen()); ASSERT_OK(shardIdentity.validate()); - serverGlobalParams.overrideShardIdentity = shardIdentity.toBSON(); + serverGlobalParams.overrideShardIdentity = shardIdentity.toShardIdentityDocument(); auto swShardingInitialized = shardingState()->initializeShardingAwarenessIfNeeded(operationContext()); @@ -380,12 +385,12 @@ TEST_F(ShardingStateTest, TEST_F(ShardingStateTest, InitializeShardingAwarenessIfNeededNotReadOnlyAndValidOverrideShardIdentity) { ShardIdentityType shardIdentity; - shardIdentity.setConfigsvrConnString( + shardIdentity.setConfigsvrConnectionString( ConnectionString(ConnectionString::SET, "a:1,b:2", "config")); shardIdentity.setShardName(kShardName); shardIdentity.setClusterId(OID::gen()); ASSERT_OK(shardIdentity.validate()); - serverGlobalParams.overrideShardIdentity = shardIdentity.toBSON(); + serverGlobalParams.overrideShardIdentity = shardIdentity.toShardIdentityDocument(); // Should error regardless of cluster role. @@ -420,8 +425,13 @@ TEST_F(ShardingStateTest, BSONObj invalidShardIdentity = BSON("_id" << "shardIdentity" - << "configsvrConnectionString" + << ShardIdentity::kShardNameFieldName + << kShardName + << ShardIdentity::kClusterIdFieldName + << OID::gen() + << ShardIdentity::kConfigsvrConnectionStringFieldName << "invalid"); + _dbDirectClient->insert(NamespaceString::kServerConfigurationNamespace.toString(), invalidShardIdentity); } @@ -442,12 +452,12 @@ TEST_F(ShardingStateTest, BSONObj validShardIdentity = [&] { ShardIdentityType shardIdentity; - shardIdentity.setConfigsvrConnString( + shardIdentity.setConfigsvrConnectionString( ConnectionString(ConnectionString::SET, "a:1,b:2", "config")); shardIdentity.setShardName(kShardName); shardIdentity.setClusterId(OID::gen()); ASSERT_OK(shardIdentity.validate()); - return shardIdentity.toBSON(); + return shardIdentity.toShardIdentityDocument(); }(); _dbDirectClient->insert(NamespaceString::kServerConfigurationNamespace.toString(), @@ -496,12 +506,12 @@ TEST_F(ShardingStateTest, BSONObj validShardIdentity = [&] { ShardIdentityType shardIdentity; - shardIdentity.setConfigsvrConnString( + shardIdentity.setConfigsvrConnectionString( ConnectionString(ConnectionString::SET, "a:1,b:2", "config")); shardIdentity.setShardName(kShardName); shardIdentity.setClusterId(OID::gen()); ASSERT_OK(shardIdentity.validate()); - return shardIdentity.toBSON(); + return shardIdentity.toShardIdentityDocument(); }(); _dbDirectClient->insert(NamespaceString::kServerConfigurationNamespace.toString(), diff --git a/src/mongo/db/s/type_shard_identity.cpp b/src/mongo/db/s/type_shard_identity.cpp index c94fa6e5892..b9e739c3bc9 100644 --- a/src/mongo/db/s/type_shard_identity.cpp +++ b/src/mongo/db/s/type_shard_identity.cpp @@ -38,122 +38,46 @@ namespace mongo { const std::string ShardIdentityType::IdName("shardIdentity"); -const BSONField<std::string> ShardIdentityType::configsvrConnString("configsvrConnectionString"); -const BSONField<std::string> ShardIdentityType::shardName("shardName"); -const BSONField<OID> ShardIdentityType::clusterId("clusterId"); - -StatusWith<ShardIdentityType> ShardIdentityType::fromBSON(const BSONObj& source) { +StatusWith<ShardIdentityType> ShardIdentityType::fromShardIdentityDocument(const BSONObj& source) { if (!source.hasField("_id")) { return {ErrorCodes::NoSuchKey, str::stream() << "missing _id field for shardIdentity document"}; } - - ShardIdentityType shardIdentity; - - { - std::string docId; - Status status = bsonExtractStringField(source, "_id", &docId); - if (!status.isOK()) { - return status; - } - - if (docId != IdName) { - return {ErrorCodes::FailedToParse, - str::stream() << "got _id: " << docId << " instead of " << IdName}; - } - } - - { - std::string connString; - Status status = bsonExtractStringField(source, configsvrConnString(), &connString); - if (!status.isOK()) { - return status; + // Strip the id field since it's always the same and we don't store it + auto shardIdentityBSON = source.removeField("_id"); + + try { + ShardIdentityType shardIdentity = + ShardIdentity::parse(IDLParserErrorContext("ShardIdentity"), shardIdentityBSON); + + const auto& configsvrConnStr = shardIdentity.getConfigsvrConnectionString(); + if (configsvrConnStr.type() != ConnectionString::SET) { + return Status(ErrorCodes::UnsupportedFormat, + str::stream() + << "config server connection string can only be replica sets: " + << configsvrConnStr.toString()); } - try { - // Note: ConnectionString::parse can uassert from HostAndPort constructor. - auto parsedConfigConnStrStatus = ConnectionString::parse(connString); - if (!parsedConfigConnStrStatus.isOK()) { - return parsedConfigConnStrStatus.getStatus(); - } - - auto configSvrConnStr = parsedConfigConnStrStatus.getValue(); - if (configSvrConnStr.type() != ConnectionString::SET) { - return Status(ErrorCodes::UnsupportedFormat, - str::stream() - << "config server connection string can only be replica sets: " - << configSvrConnStr.toString()); - } - - shardIdentity.setConfigsvrConnString(std::move(configSvrConnStr)); - } catch (const AssertionException& parseException) { - return parseException.toStatus(); - } + return shardIdentity; + } catch (const AssertionException& parseException) { + return parseException.toStatus(); } - - { - std::string name; - Status status = bsonExtractStringField(source, shardName(), &name); - if (!status.isOK()) { - return status; - } - - shardIdentity.setShardName(name); - } - - { - OID oid; - Status status = bsonExtractOIDField(source, clusterId(), &oid); - if (!status.isOK()) { - return status; - } - - shardIdentity.setClusterId(oid); - } - - return shardIdentity; } Status ShardIdentityType::validate() const { - if (!_configsvrConnString) { - return {ErrorCodes::NoSuchKey, - str::stream() << "missing " << configsvrConnString() << " field"}; - } - - if (_configsvrConnString->type() != ConnectionString::SET) { + const auto& configsvrConnStr = getConfigsvrConnectionString(); + if (configsvrConnStr.type() != ConnectionString::SET) { return {ErrorCodes::UnsupportedFormat, str::stream() << "config connection string can only be replica sets, got " - << ConnectionString::typeToString(_configsvrConnString->type())}; - } - - if (!_shardName || _shardName->empty()) { - return {ErrorCodes::NoSuchKey, str::stream() << "missing " << shardName() << " field"}; - } - - if (!_clusterId || !_clusterId->isSet()) { - return {ErrorCodes::NoSuchKey, str::stream() << "missing " << clusterId() << " field"}; + << ConnectionString::typeToString(configsvrConnStr.type())}; } - return Status::OK(); } -BSONObj ShardIdentityType::toBSON() const { +BSONObj ShardIdentityType::toShardIdentityDocument() const { BSONObjBuilder builder; - - builder.append("_id", IdName); - - if (_configsvrConnString) { - builder << configsvrConnString(_configsvrConnString->toString()); - } - - if (_shardName) { - builder << shardName(_shardName.get()); - } - - if (_clusterId) { - builder << clusterId(_clusterId.get()); - } - + builder.append("_id", ShardIdentityType::IdName); + builder.appendElements(ShardIdentity::toBSON()); return builder.obj(); } @@ -161,49 +85,10 @@ std::string ShardIdentityType::toString() const { return toBSON().toString(); } -bool ShardIdentityType::isConfigsvrConnStringSet() const { - return _configsvrConnString.is_initialized(); -} - -const ConnectionString& ShardIdentityType::getConfigsvrConnString() const { - invariant(_configsvrConnString); - return _configsvrConnString.get(); -} - -void ShardIdentityType::setConfigsvrConnString(ConnectionString connString) { - _configsvrConnString = std::move(connString); -} - -bool ShardIdentityType::isShardNameSet() const { - return _shardName.is_initialized(); -} - -const std::string& ShardIdentityType::getShardName() const { - invariant(_shardName); - return _shardName.get(); -} - -void ShardIdentityType::setShardName(std::string shardName) { - _shardName = std::move(shardName); -} - -bool ShardIdentityType::isClusterIdSet() const { - return _clusterId.is_initialized(); -} - -const OID& ShardIdentityType::getClusterId() const { - invariant(_clusterId); - return _clusterId.get(); -} - -void ShardIdentityType::setClusterId(OID clusterId) { - _clusterId = std::move(clusterId); -} - BSONObj ShardIdentityType::createConfigServerUpdateObject(const std::string& newConnString) { BSONObjBuilder builder; BSONObjBuilder setConfigBuilder(builder.subobjStart("$set")); - setConfigBuilder.append(configsvrConnString(), newConnString); + setConfigBuilder.append(ShardIdentity::kConfigsvrConnectionStringFieldName, newConnString); setConfigBuilder.doneFast(); return builder.obj(); } diff --git a/src/mongo/db/s/type_shard_identity.h b/src/mongo/db/s/type_shard_identity.h index a46049dd287..b29a8c48a63 100644 --- a/src/mongo/db/s/type_shard_identity.h +++ b/src/mongo/db/s/type_shard_identity.h @@ -32,73 +32,48 @@ #include "mongo/client/connection_string.h" #include "mongo/db/jsobj.h" +#include "mongo/db/s/add_shard_cmd_gen.h" namespace mongo { /** * Contains all the information needed to make a mongod instance shard aware. */ -class ShardIdentityType { +class ShardIdentityType : public ShardIdentity { public: // The _id value for this document type. static const std::string IdName; - // Field names and types in a shardIdentity document. - static const BSONField<std::string> configsvrConnString; - static const BSONField<std::string> shardName; - static const BSONField<OID> clusterId; - ShardIdentityType() = default; + ShardIdentityType(const ShardIdentity& sid) : ShardIdentity(sid) {} /** - * Constructs a new ShardIdentityType object from BSON. - * Also does validation of the contents. - */ - static StatusWith<ShardIdentityType> fromBSON(const BSONObj& source); - - /** - * Returns OK if all fields have been set. Otherwise, returns NoSuchKey - * and information about the first field that is missing. + * Constructs a new ShardIdentityType object from a BSON object containing + * a shard identity document. Also does validation of the contents. */ - Status validate() const; + static StatusWith<ShardIdentityType> fromShardIdentityDocument(const BSONObj& source); /** - * Returns the BSON representation of the entry. + * Returns the BSON representation of the entry as a shard identity document. */ - BSONObj toBSON() const; + BSONObj toShardIdentityDocument() const; /** * Returns a std::string representation of the current internal state. */ std::string toString() const; - bool isConfigsvrConnStringSet() const; - const ConnectionString& getConfigsvrConnString() const; - void setConfigsvrConnString(ConnectionString connString); - - bool isShardNameSet() const; - const std::string& getShardName() const; - void setShardName(std::string shardName); - - bool isClusterIdSet() const; - const OID& getClusterId() const; - void setClusterId(OID clusterId); + /** + * Returns OK if all fields have been set. Otherwise, returns NoSuchKey + * and information about the first field that is missing. + */ + Status validate() const; /** * Returns an update object that can be used to update the config server field of the * shardIdentity document with the new connection string. */ static BSONObj createConfigServerUpdateObject(const std::string& newConnString); - -private: - // Convention: (M)andatory, (O)ptional, (S)pecial rule. - - // (M) connection string to the config server. - boost::optional<ConnectionString> _configsvrConnString; - // (M) contains the name of the shard. - boost::optional<std::string> _shardName; - // (M) contains the (unique) identifier of the cluster. - boost::optional<OID> _clusterId; }; } // namespace mongo diff --git a/src/mongo/db/s/type_shard_identity_test.cpp b/src/mongo/db/s/type_shard_identity_test.cpp index cb9af748106..4fdb1e523de 100644 --- a/src/mongo/db/s/type_shard_identity_test.cpp +++ b/src/mongo/db/s/type_shard_identity_test.cpp @@ -43,25 +43,22 @@ TEST(ShardIdentityType, RoundTrip) { auto clusterId(OID::gen()); auto doc = BSON("_id" << "shardIdentity" - << "configsvrConnectionString" - << "test/a:123" << "shardName" << "s1" << "clusterId" - << clusterId); + << clusterId + << "configsvrConnectionString" + << "test/a:123"); - auto result = ShardIdentityType::fromBSON(doc); + auto result = ShardIdentityType::fromShardIdentityDocument(doc); ASSERT_OK(result.getStatus()); auto shardIdentity = result.getValue(); - ASSERT_TRUE(shardIdentity.isConfigsvrConnStringSet()); - ASSERT_EQ("test/a:123", shardIdentity.getConfigsvrConnString().toString()); - ASSERT_TRUE(shardIdentity.isShardNameSet()); + ASSERT_EQ("test/a:123", shardIdentity.getConfigsvrConnectionString().toString()); ASSERT_EQ("s1", shardIdentity.getShardName()); - ASSERT_TRUE(shardIdentity.isClusterIdSet()); ASSERT_EQ(clusterId, shardIdentity.getClusterId()); - ASSERT_BSONOBJ_EQ(doc, shardIdentity.toBSON()); + ASSERT_BSONOBJ_EQ(doc, shardIdentity.toShardIdentityDocument()); } TEST(ShardIdentityType, ParseMissingId) { @@ -72,7 +69,7 @@ TEST(ShardIdentityType, ParseMissingId) { << "clusterId" << OID::gen()); - auto result = ShardIdentityType::fromBSON(doc); + auto result = ShardIdentityType::fromShardIdentityDocument(doc); ASSERT_NOT_OK(result.getStatus()); } @@ -84,7 +81,7 @@ TEST(ShardIdentityType, ParseMissingConfigsvrConnString) { << "clusterId" << OID::gen()); - auto result = ShardIdentityType::fromBSON(doc); + auto result = ShardIdentityType::fromShardIdentityDocument(doc); ASSERT_NOT_OK(result.getStatus()); } @@ -96,7 +93,7 @@ TEST(ShardIdentityType, ParseMissingShardName) { << "clusterId" << OID::gen()); - auto result = ShardIdentityType::fromBSON(doc); + auto result = ShardIdentityType::fromShardIdentityDocument(doc); ASSERT_NOT_OK(result.getStatus()); } @@ -108,7 +105,7 @@ TEST(ShardIdentityType, ParseMissingClusterId) { << "shardName" << "s1"); - auto result = ShardIdentityType::fromBSON(doc); + auto result = ShardIdentityType::fromShardIdentityDocument(doc); ASSERT_NOT_OK(result.getStatus()); } @@ -123,7 +120,8 @@ TEST(ShardIdentityType, InvalidConnectionString) { << "clusterId" << clusterId); - ASSERT_EQ(ErrorCodes::FailedToParse, ShardIdentityType::fromBSON(doc).getStatus()); + ASSERT_EQ(ErrorCodes::FailedToParse, + ShardIdentityType::fromShardIdentityDocument(doc).getStatus()); } TEST(ShardIdentityType, NonReplSetConnectionString) { @@ -137,7 +135,8 @@ TEST(ShardIdentityType, NonReplSetConnectionString) { << "clusterId" << clusterId); - ASSERT_EQ(ErrorCodes::UnsupportedFormat, ShardIdentityType::fromBSON(doc).getStatus()); + ASSERT_EQ(ErrorCodes::UnsupportedFormat, + ShardIdentityType::fromShardIdentityDocument(doc).getStatus()); } TEST(ShardIdentityType, CreateUpdateObject) { |