summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Saltz <matthew.saltz@mongodb.com>2018-06-07 14:35:58 -0400
committerMatthew Saltz <matthew.saltz@mongodb.com>2018-06-13 17:02:08 -0400
commit09919208ae16cd44f3ca3e98220e1cc322419a8a (patch)
treee0830b8d56407a2131f0b5e09bf489ea42d848e7
parent85477a157ab217912eec55422a9d7b2d0e7b0d47 (diff)
downloadmongo-09919208ae16cd44f3ca3e98220e1cc322419a8a.tar.gz
SERVER-35486 Create _addShard command on shard to drive shard initialization
-rw-r--r--jstests/auth/lib/commands_lib.js22
-rw-r--r--jstests/core/views/views_all_commands.js1
-rw-r--r--jstests/sharding/safe_secondary_reads_drop_recreate.js1
-rw-r--r--jstests/sharding/safe_secondary_reads_single_migration_suspend_range_deletion.js1
-rw-r--r--jstests/sharding/safe_secondary_reads_single_migration_waitForDelete.js1
-rw-r--r--src/mongo/client/connection_string.cpp4
-rw-r--r--src/mongo/client/connection_string.h6
-rw-r--r--src/mongo/db/s/SConscript3
-rw-r--r--src/mongo/db/s/add_shard_cmd.cpp103
-rw-r--r--src/mongo/db/s/add_shard_cmd.idl53
-rw-r--r--src/mongo/db/s/add_shard_util.cpp80
-rw-r--r--src/mongo/db/s/add_shard_util.h63
-rw-r--r--src/mongo/db/s/collection_sharding_state_test.cpp19
-rw-r--r--src/mongo/db/s/config/sharding_catalog_manager.h8
-rw-r--r--src/mongo/db/s/config/sharding_catalog_manager_add_shard_test.cpp98
-rw-r--r--src/mongo/db/s/config/sharding_catalog_manager_shard_operations.cpp86
-rw-r--r--src/mongo/db/s/shard_server_op_observer.cpp2
-rw-r--r--src/mongo/db/s/sharding_state.cpp10
-rw-r--r--src/mongo/db/s/sharding_state_test.cpp52
-rw-r--r--src/mongo/db/s/type_shard_identity.cpp163
-rw-r--r--src/mongo/db/s/type_shard_identity.h51
-rw-r--r--src/mongo/db/s/type_shard_identity_test.cpp29
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) {