summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaloian Manassiev <kaloian.manassiev@mongodb.com>2015-07-28 12:57:01 -0400
committerKaloian Manassiev <kaloian.manassiev@mongodb.com>2015-07-29 10:08:45 -0400
commitb0067ef44c6461beed332c8bbfc40ceb55d05d40 (patch)
tree9171f3e4f363481238b1a3e5d3edca790d8cd255
parent6c88ec9513f3d400695301b3af15de2721748bc2 (diff)
downloadmongo-b0067ef44c6461beed332c8bbfc40ceb55d05d40.tar.gz
SERVER-19319 Send setShardVersion command after sharding a collection
The shard collection operation needs to inform the former primary shard for the database containing this collection that the collection is being sharded. That way, unsharded operations will be rejected when they come from mongos instances, which are not yet aware the collection became sharded. Also reverts commit 280b8e6c835bab826899a7696a1f5852e1221d53 ("SERVER-19319 Temporarily update some tests to run a query after sharding a collection to force the version to be set")
-rw-r--r--src/mongo/s/SConscript22
-rw-r--r--src/mongo/s/catalog/replset/catalog_manager_replica_set.cpp30
-rw-r--r--src/mongo/s/catalog/replset/catalog_manager_replica_set_shard_collection_test.cpp41
-rw-r--r--src/mongo/s/set_shard_version_request.cpp214
-rw-r--r--src/mongo/s/set_shard_version_request.h147
-rw-r--r--src/mongo/s/set_shard_version_request_test.cpp219
6 files changed, 653 insertions, 20 deletions
diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript
index 1669b744cbe..705f48744c9 100644
--- a/src/mongo/s/SConscript
+++ b/src/mongo/s/SConscript
@@ -32,9 +32,11 @@ env.Library(
target='common',
source=[
'chunk_diff.cpp',
+ 'set_shard_version_request.cpp',
],
LIBDEPS=[
'catalog/catalog_types',
+ '$BUILD_DIR/mongo/client/connection_string',
]
)
@@ -49,23 +51,29 @@ env.Library(
)
env.CppUnitTest(
+ target='chunk_diff_test',
+ source=[
+ 'chunk_diff_test.cpp',
+ ],
+ LIBDEPS=[
+ 'common',
+ ]
+)
+
+env.CppUnitTest(
target='chunk_version_test',
source=[
'chunk_version_test.cpp',
],
LIBDEPS=[
- '$BUILD_DIR/mongo/base',
- '$BUILD_DIR/mongo/client/clientdriver',
- '$BUILD_DIR/mongo/db/common',
- '$BUILD_DIR/mongo/db/range_arithmetic',
- 'catalog/catalog_types',
+ 'common',
]
)
env.CppUnitTest(
- target='chunk_diff_test',
+ target='set_shard_version_request_test',
source=[
- 'chunk_diff_test.cpp',
+ 'set_shard_version_request_test.cpp',
],
LIBDEPS=[
'common',
diff --git a/src/mongo/s/catalog/replset/catalog_manager_replica_set.cpp b/src/mongo/s/catalog/replset/catalog_manager_replica_set.cpp
index 99b797b0336..5204bca6432 100644
--- a/src/mongo/s/catalog/replset/catalog_manager_replica_set.cpp
+++ b/src/mongo/s/catalog/replset/catalog_manager_replica_set.cpp
@@ -63,6 +63,7 @@
#include "mongo/s/chunk_manager.h"
#include "mongo/s/config.h"
#include "mongo/s/grid.h"
+#include "mongo/s/set_shard_version_request.h"
#include "mongo/s/shard_key_pattern.h"
#include "mongo/s/write_ops/batched_command_request.h"
#include "mongo/s/write_ops/batched_command_response.h"
@@ -211,13 +212,28 @@ Status CatalogManagerReplicaSet::shardCollection(OperationContext* txn,
collInfo.save(ns);
manager->reload(true);
- // TODO(spencer) SERVER-19319: Send setShardVersion to primary shard so it knows to start
- // rejecting unversioned writes.
-
- BSONObj finishDetail = BSON("version"
- << ""); // TODO(spencer) SERVER-19319 Report actual version used
-
- logChange(txn->getClient()->clientAddress(true), "shardCollection", ns, finishDetail);
+ // Tell the primary mongod to refresh its data
+ // TODO: Think the real fix here is for mongos to just
+ // assume that all collections are sharded, when we get there
+ SetShardVersionRequest ssv =
+ SetShardVersionRequest::makeForVersioning(_configServerConnectionString,
+ dbPrimaryShardId,
+ primaryShard->getConnString(),
+ NamespaceString(ns),
+ manager->getVersion(),
+ true);
+
+ auto ssvStatus = grid.shardRegistry()->runCommandWithNotMasterRetries(
+ dbPrimaryShardId, "admin", ssv.toBSON());
+ if (!ssvStatus.isOK()) {
+ warning() << "could not update initial version of " << ns << " on shard primary "
+ << dbPrimaryShardId << ssvStatus.getStatus();
+ }
+
+ logChange(txn->getClient()->clientAddress(true),
+ "shardCollection",
+ ns,
+ BSON("version" << manager->getVersion().toString()));
return Status::OK();
}
diff --git a/src/mongo/s/catalog/replset/catalog_manager_replica_set_shard_collection_test.cpp b/src/mongo/s/catalog/replset/catalog_manager_replica_set_shard_collection_test.cpp
index c366a48aee5..05a73346816 100644
--- a/src/mongo/s/catalog/replset/catalog_manager_replica_set_shard_collection_test.cpp
+++ b/src/mongo/s/catalog/replset/catalog_manager_replica_set_shard_collection_test.cpp
@@ -49,6 +49,7 @@
#include "mongo/s/chunk.h"
#include "mongo/s/client/shard_registry.h"
#include "mongo/s/grid.h"
+#include "mongo/s/set_shard_version_request.h"
#include "mongo/s/shard_key_pattern.h"
#include "mongo/s/write_ops/batched_command_request.h"
#include "mongo/s/write_ops/batched_command_response.h"
@@ -237,6 +238,27 @@ public:
});
}
+ void expectSetShardVersion(const HostAndPort& expectedTargetHost,
+ const string& expectedNs,
+ const ChunkVersion& expectedChunkVersion) {
+ onCommand([&](const RemoteCommandRequest& request) {
+ ASSERT_EQ(expectedTargetHost, request.target);
+
+ SetShardVersionRequest ssv =
+ assertGet(SetShardVersionRequest::parseFromBSON(request.cmdObj));
+
+ ASSERT(!ssv.isInit());
+ ASSERT(ssv.isAuthoritative());
+ ASSERT_EQ(catalogManager()->connectionString().toString(),
+ ssv.getConfigServer().toString());
+ ASSERT_EQ(expectedTargetHost.toString(), ssv.getShardConnectionString().toString());
+ ASSERT_EQ(expectedNs, ssv.getNS().ns());
+ ASSERT_EQ(expectedChunkVersion.toString(), ssv.getNSVersion().toString());
+
+ return BSON("ok" << true);
+ });
+ }
+
protected:
const HostAndPort configHost{"configHost1"};
const HostAndPort clientHost{"clientHost1"};
@@ -404,14 +426,16 @@ TEST_F(ShardCollectionTest, noInitialChunksOrData) {
expectReloadChunks(ns, {expectedChunk});
expectLoadNewestChunk(ns, expectedChunk);
+ // Expect the set shard version for that namespace
+ expectSetShardVersion(shardHost, ns, actualVersion);
+
// Respond to request to write final changelog entry indicating success.
expectChangeLogInsert(configHost,
clientHost.toString(),
network()->now(),
"shardCollection",
ns,
- BSON("version"
- << ""));
+ BSON("version" << actualVersion.toString()));
future.timed_get(kFutureTimeout);
}
@@ -421,6 +445,7 @@ TEST_F(ShardCollectionTest, withInitialChunks) {
const HostAndPort shard0Host{"shardHost0"};
const HostAndPort shard1Host{"shardHost1"};
const HostAndPort shard2Host{"shardHost2"};
+
ShardType shard0;
shard0.setName("shard0");
shard0.setHost(shard0Host.toString());
@@ -581,14 +606,16 @@ TEST_F(ShardCollectionTest, withInitialChunks) {
expectReloadChunks(ns, expectedChunks);
expectLoadNewestChunk(ns, expectedChunks[4]);
+ // Expect the set shard version for that namespace
+ expectSetShardVersion(shard0Host, ns, expectedChunks[4].getVersion());
+
// Respond to request to write final changelog entry indicating success.
expectChangeLogInsert(configHost,
clientHost.toString(),
network()->now(),
"shardCollection",
ns,
- BSON("version"
- << ""));
+ BSON("version" << expectedChunks[4].getVersion().toString()));
future.timed_get(kFutureTimeout);
}
@@ -759,14 +786,16 @@ TEST_F(ShardCollectionTest, withInitialData) {
expectReloadChunks(ns, expectedChunks);
expectLoadNewestChunk(ns, expectedChunks[4]);
+ // Expect the set shard version for that namespace
+ expectSetShardVersion(shardHost, ns, expectedChunks[4].getVersion());
+
// Respond to request to write final changelog entry indicating success.
expectChangeLogInsert(configHost,
clientHost.toString(),
network()->now(),
"shardCollection",
ns,
- BSON("version"
- << ""));
+ BSON("version" << expectedChunks[4].getVersion().toString()));
future.timed_get(kFutureTimeout);
}
diff --git a/src/mongo/s/set_shard_version_request.cpp b/src/mongo/s/set_shard_version_request.cpp
new file mode 100644
index 00000000000..5c869c22f01
--- /dev/null
+++ b/src/mongo/s/set_shard_version_request.cpp
@@ -0,0 +1,214 @@
+/**
+ * Copyright (C) 2015 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/s/set_shard_version_request.h"
+
+#include "mongo/base/status_with.h"
+#include "mongo/bson/bsonobj.h"
+#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/bson/util/bson_extract.h"
+#include "mongo/util/assert_util.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+namespace {
+
+const char kCmdName[] = "setShardVersion";
+const char kConfigServer[] = "configdb";
+const char kShardName[] = "shard";
+const char kShardConnectionString[] = "shardHost";
+const char kInit[] = "init";
+const char kAuthoritative[] = "authoritative";
+const char kVersion[] = "version";
+
+} // namespace
+
+SetShardVersionRequest::SetShardVersionRequest(ConnectionString configServer,
+ std::string shardName,
+ ConnectionString shardConnectionString)
+ : _init(true),
+ _configServer(std::move(configServer)),
+ _shardName(std::move(shardName)),
+ _shardCS(std::move(shardConnectionString)) {}
+
+SetShardVersionRequest::SetShardVersionRequest(ConnectionString configServer,
+ std::string shardName,
+ ConnectionString shardConnectionString,
+ NamespaceString nss,
+ ChunkVersion version,
+ bool isAuthoritative)
+ : _init(false),
+ _configServer(std::move(configServer)),
+ _shardName(std::move(shardName)),
+ _shardCS(std::move(shardConnectionString)),
+ _nss(std::move(nss)),
+ _version(std::move(version)),
+ _isAuthoritative(isAuthoritative) {}
+
+SetShardVersionRequest::SetShardVersionRequest() = default;
+
+SetShardVersionRequest SetShardVersionRequest::makeForInit(
+ const ConnectionString& configServer,
+ const std::string& shardName,
+ const ConnectionString& shardConnectionString) {
+ return SetShardVersionRequest(configServer, shardName, shardConnectionString);
+}
+
+SetShardVersionRequest SetShardVersionRequest::makeForVersioning(
+ const ConnectionString& configServer,
+ const std::string& shardName,
+ const ConnectionString& shardConnectionString,
+ const NamespaceString& nss,
+ const ChunkVersion& nssVersion,
+ bool isAuthoritative) {
+ return SetShardVersionRequest(
+ configServer, shardName, shardConnectionString, nss, nssVersion, isAuthoritative);
+}
+
+StatusWith<SetShardVersionRequest> SetShardVersionRequest::parseFromBSON(const BSONObj& cmdObj) {
+ SetShardVersionRequest request;
+
+ {
+ std::string configServer;
+ Status status = bsonExtractStringField(cmdObj, kConfigServer, &configServer);
+ if (!status.isOK())
+ return status;
+
+ auto configServerStatus = ConnectionString::parse(configServer);
+ if (!configServerStatus.isOK())
+ return configServerStatus.getStatus();
+
+ request._configServer = std::move(configServerStatus.getValue());
+ }
+
+ {
+ Status status = bsonExtractStringField(cmdObj, kShardName, &request._shardName);
+ if (!status.isOK())
+ return status;
+ }
+
+ {
+ std::string shardCS;
+ Status status = bsonExtractStringField(cmdObj, kShardConnectionString, &shardCS);
+ if (!status.isOK())
+ return status;
+
+ auto shardCSStatus = ConnectionString::parse(shardCS);
+ if (!shardCSStatus.isOK())
+ return shardCSStatus.getStatus();
+
+ request._shardCS = std::move(shardCSStatus.getValue());
+ }
+
+ {
+ Status status = bsonExtractBooleanFieldWithDefault(cmdObj, kInit, false, &request._init);
+ if (!status.isOK())
+ return status;
+ }
+
+ if (request.isInit()) {
+ return request;
+ }
+
+ // Only initialize the version information if this is not an "init" request
+
+ {
+ std::string ns;
+ Status status = bsonExtractStringField(cmdObj, kCmdName, &ns);
+ if (!status.isOK())
+ return status;
+
+ NamespaceString nss(ns);
+
+ if (!nss.isValid()) {
+ return {ErrorCodes::InvalidNamespace,
+ str::stream() << ns << " is not a valid namespace"};
+ }
+
+ request._nss = std::move(nss);
+ }
+
+ {
+ bool canParse;
+
+ ChunkVersion chunkVersion = ChunkVersion::fromBSON(cmdObj, kVersion, &canParse);
+ if (!canParse) {
+ return {ErrorCodes::BadValue, "Unable to parse shard version"};
+ }
+
+ request._version = std::move(chunkVersion);
+ }
+
+ {
+ bool isAuthoritative;
+ Status status =
+ bsonExtractBooleanFieldWithDefault(cmdObj, kAuthoritative, false, &isAuthoritative);
+ if (!status.isOK())
+ return status;
+
+ request._isAuthoritative = isAuthoritative;
+ }
+
+ return request;
+}
+
+BSONObj SetShardVersionRequest::toBSON() const {
+ BSONObjBuilder cmdBuilder;
+
+ cmdBuilder.append(kCmdName, _init ? "" : _nss.get().ns());
+ cmdBuilder.append(kInit, _init);
+ cmdBuilder.append(kConfigServer, _configServer.toString());
+ cmdBuilder.append(kShardName, _shardName);
+ cmdBuilder.append(kShardConnectionString, _shardCS.toString());
+
+ if (!_init) {
+ _version.get().addToBSON(cmdBuilder, kVersion);
+ cmdBuilder.append(kAuthoritative, isAuthoritative());
+ }
+
+ return cmdBuilder.obj();
+}
+
+const NamespaceString& SetShardVersionRequest::getNS() const {
+ invariant(!_init);
+ return _nss.get();
+}
+
+const ChunkVersion SetShardVersionRequest::getNSVersion() const {
+ invariant(!_init);
+ return _version.get();
+}
+
+bool SetShardVersionRequest::isAuthoritative() const {
+ invariant(!_init);
+ return _isAuthoritative.get_value_or(false);
+}
+
+} // namespace mongo
diff --git a/src/mongo/s/set_shard_version_request.h b/src/mongo/s/set_shard_version_request.h
new file mode 100644
index 00000000000..881c2a3bfe5
--- /dev/null
+++ b/src/mongo/s/set_shard_version_request.h
@@ -0,0 +1,147 @@
+/**
+ * Copyright (C) 2015 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 <boost/optional.hpp>
+#include <string>
+
+#include "mongo/client/connection_string.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/s/chunk_version.h"
+
+namespace mongo {
+
+class BSONObj;
+struct ChunkVersion;
+template <typename T>
+class StatusWith;
+
+/**
+ * Encapsulates the parsing and construction logic for the SetShardVersion command.
+ */
+class SetShardVersionRequest {
+public:
+ /**
+ * Constructs a new set shard version request, which is of the "init" type, meaning it has no
+ * namespace or version information associated with it and the init flag is set.
+ */
+ static SetShardVersionRequest makeForInit(const ConnectionString& configServer,
+ const std::string& shardName,
+ const ConnectionString& shardConnectionString);
+
+ /**
+ * Constructs a new set shard version request, which is of the "versioning" type, meaning it has
+ * both initialization data and namespace and version information associated with it.
+ */
+ static SetShardVersionRequest makeForVersioning(const ConnectionString& configServer,
+ const std::string& shardName,
+ const ConnectionString& shard,
+ const NamespaceString& nss,
+ const ChunkVersion& nssVersion,
+ bool isAuthoritative);
+
+ /**
+ * Parses an SSV request from a set shard version command.
+ */
+ static StatusWith<SetShardVersionRequest> parseFromBSON(const BSONObj& cmdObj);
+
+ /**
+ * Produces a BSON representation of the request, which can be used for sending as a command.
+ */
+ BSONObj toBSON() const;
+
+ /**
+ * Returns whether this is an "init" type of request, where we only have the config server
+ * information and the identity that the targeted shard should assume or it contains namespace
+ * version as well. If this value is true, it is illegal to access anything other than the
+ * config server, shard name and shard connection string fields.
+ */
+ bool isInit() const {
+ return _init;
+ }
+
+ const ConnectionString& getConfigServer() const {
+ return _configServer;
+ }
+
+ const std::string& getShardName() const {
+ return _shardName;
+ }
+
+ const ConnectionString& getShardConnectionString() const {
+ return _shardCS;
+ }
+
+ /**
+ * Returns the namespace associated with this set shard version request. It is illegal to access
+ * this field if isInit() returns true.
+ */
+ const NamespaceString& getNS() const;
+
+ /**
+ * Returns the version of the namespace associated with this set shard version request. It is
+ * illegal to access this field if isInit() returns true.
+ */
+ const ChunkVersion getNSVersion() const;
+
+ /**
+ * Returns whether this request should force the version to be set instead of it being reloaded
+ * and recalculated from the metadata. It is illegal to access this field if isInit() returns
+ * true.
+ */
+ bool isAuthoritative() const;
+
+private:
+ SetShardVersionRequest(ConnectionString configServer,
+ std::string shardName,
+ ConnectionString shardConnectionString);
+
+ SetShardVersionRequest(ConnectionString configServer,
+ std::string shardName,
+ ConnectionString shardConnectionString,
+ NamespaceString nss,
+ ChunkVersion nssVersion,
+ bool isAuthoritative);
+
+ SetShardVersionRequest();
+
+ bool _init{false};
+
+ ConnectionString _configServer;
+
+ std::string _shardName;
+ ConnectionString _shardCS;
+
+ // These values are only set if _init is false
+ boost::optional<NamespaceString> _nss;
+ boost::optional<ChunkVersion> _version;
+ boost::optional<bool> _isAuthoritative;
+};
+
+} // namespace mongo
diff --git a/src/mongo/s/set_shard_version_request_test.cpp b/src/mongo/s/set_shard_version_request_test.cpp
new file mode 100644
index 00000000000..a71026e67d0
--- /dev/null
+++ b/src/mongo/s/set_shard_version_request_test.cpp
@@ -0,0 +1,219 @@
+/**
+ * Copyright (C) 2015 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/bson/bsonmisc.h"
+#include "mongo/bson/oid.h"
+#include "mongo/s/set_shard_version_request.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+
+using unittest::assertGet;
+
+namespace {
+
+const ConnectionString configCS = ConnectionString::forReplicaSet(
+ "ConfigRS", {HostAndPort{"configHost1:27017"}, HostAndPort{"configHost2:27017"}});
+
+const ConnectionString shardCS = ConnectionString::forReplicaSet(
+ "ShardRS", {HostAndPort{"shardHost1:12345"}, HostAndPort{"shardHost2:12345"}});
+
+TEST(SetShardVersionRequest, ParseInit) {
+ SetShardVersionRequest request =
+ assertGet(SetShardVersionRequest::parseFromBSON(
+ BSON("setShardVersion"
+ << ""
+ << "init" << true << "configdb" << configCS.toString() << "shard"
+ << "TestShard"
+ << "shardHost" << shardCS.toString())));
+
+ ASSERT(request.isInit());
+ ASSERT_EQ(request.getConfigServer().toString(), configCS.toString());
+ ASSERT_EQ(request.getShardName(), "TestShard");
+ ASSERT_EQ(request.getShardConnectionString().toString(), shardCS.toString());
+}
+
+TEST(SetShardVersionRequest, ParseFull) {
+ const ChunkVersion chunkVersion(1, 2, OID::gen());
+
+ SetShardVersionRequest request =
+ assertGet(SetShardVersionRequest::parseFromBSON(
+ BSON("setShardVersion"
+ << "db.coll"
+ << "configdb" << configCS.toString() << "shard"
+ << "TestShard"
+ << "shardHost" << shardCS.toString() << "version"
+ << Timestamp(chunkVersion.toLong()) << "versionEpoch" << chunkVersion.epoch())));
+
+ ASSERT(!request.isInit());
+ ASSERT(!request.isAuthoritative());
+ ASSERT_EQ(request.getConfigServer().toString(), configCS.toString());
+ ASSERT_EQ(request.getShardName(), "TestShard");
+ ASSERT_EQ(request.getShardConnectionString().toString(), shardCS.toString());
+ ASSERT_EQ(request.getNS().toString(), "db.coll");
+ ASSERT_EQ(request.getNSVersion().majorVersion(), chunkVersion.majorVersion());
+ ASSERT_EQ(request.getNSVersion().minorVersion(), chunkVersion.minorVersion());
+ ASSERT_EQ(request.getNSVersion().epoch(), chunkVersion.epoch());
+}
+
+TEST(SetShardVersionRequest, ParseFullAuhtoritative) {
+ const ChunkVersion chunkVersion(1, 2, OID::gen());
+
+ SetShardVersionRequest request =
+ assertGet(SetShardVersionRequest::parseFromBSON(
+ BSON("setShardVersion"
+ << "db.coll"
+ << "configdb" << configCS.toString() << "shard"
+ << "TestShard"
+ << "shardHost" << shardCS.toString() << "version"
+ << Timestamp(chunkVersion.toLong()) << "versionEpoch" << chunkVersion.epoch()
+ << "authoritative" << true)));
+
+ ASSERT(!request.isInit());
+ ASSERT(request.isAuthoritative());
+ ASSERT_EQ(request.getConfigServer().toString(), configCS.toString());
+ ASSERT_EQ(request.getShardName(), "TestShard");
+ ASSERT_EQ(request.getShardConnectionString().toString(), shardCS.toString());
+ ASSERT_EQ(request.getNS().toString(), "db.coll");
+ ASSERT_EQ(request.getNSVersion().majorVersion(), chunkVersion.majorVersion());
+ ASSERT_EQ(request.getNSVersion().minorVersion(), chunkVersion.minorVersion());
+ ASSERT_EQ(request.getNSVersion().epoch(), chunkVersion.epoch());
+}
+
+TEST(SetShardVersionRequest, ParseInitNoConfigServer) {
+ auto ssvStatus =
+ SetShardVersionRequest::parseFromBSON(BSON("setShardVersion"
+ << ""
+ << "init" << true << "shard"
+ << "TestShard"
+ << "shardHost" << shardCS.toString()));
+
+ ASSERT_EQ(ErrorCodes::NoSuchKey, ssvStatus.getStatus().code());
+}
+
+TEST(SetShardVersionRequest, ParseFullNoNS) {
+ const ChunkVersion chunkVersion(1, 2, OID::gen());
+
+ auto ssvStatus =
+ SetShardVersionRequest::parseFromBSON(BSON("setShardVersion"
+ << ""
+ << "configdb" << configCS.toString() << "shard"
+ << "TestShard"
+ << "shardHost" << shardCS.toString() << "version"
+ << Timestamp(chunkVersion.toLong())
+ << "versionEpoch" << chunkVersion.epoch()));
+
+ ASSERT_EQ(ErrorCodes::InvalidNamespace, ssvStatus.getStatus().code());
+}
+
+TEST(SetShardVersionRequest, ParseFullNSContainsDBOnly) {
+ const ChunkVersion chunkVersion(1, 2, OID::gen());
+
+ auto ssvStatus =
+ SetShardVersionRequest::parseFromBSON(BSON("setShardVersion"
+ << "dbOnly"
+ << "configdb" << configCS.toString() << "shard"
+ << "TestShard"
+ << "shardHost" << shardCS.toString() << "version"
+ << Timestamp(chunkVersion.toLong())
+ << "versionEpoch" << chunkVersion.epoch()));
+
+ ASSERT_EQ(ErrorCodes::InvalidNamespace, ssvStatus.getStatus().code());
+}
+
+TEST(SetShardVersionRequest, ToSSVCommandInit) {
+ SetShardVersionRequest ssv =
+ SetShardVersionRequest::makeForInit(configCS, "TestShard", shardCS);
+
+ ASSERT(ssv.isInit());
+ ASSERT_EQ(ssv.getConfigServer().toString(), configCS.toString());
+ ASSERT_EQ(ssv.getShardName(), "TestShard");
+ ASSERT_EQ(ssv.getShardConnectionString().toString(), shardCS.toString());
+
+ ASSERT_EQ(ssv.toBSON(),
+ BSON("setShardVersion"
+ << ""
+ << "init" << true << "configdb" << configCS.toString() << "shard"
+ << "TestShard"
+ << "shardHost" << shardCS.toString()));
+}
+
+TEST(SetShardVersionRequest, ToSSVCommandFull) {
+ const ChunkVersion chunkVersion(1, 2, OID::gen());
+
+ SetShardVersionRequest ssv = SetShardVersionRequest::makeForVersioning(
+ configCS, "TestShard", shardCS, NamespaceString("db.coll"), chunkVersion, false);
+
+ ASSERT(!ssv.isInit());
+ ASSERT(!ssv.isAuthoritative());
+ ASSERT_EQ(ssv.getConfigServer().toString(), configCS.toString());
+ ASSERT_EQ(ssv.getShardName(), "TestShard");
+ ASSERT_EQ(ssv.getShardConnectionString().toString(), shardCS.toString());
+ ASSERT_EQ(ssv.getNS().ns(), "db.coll");
+ ASSERT_EQ(ssv.getNSVersion().toBSONWithPrefix("version"),
+ chunkVersion.toBSONWithPrefix("version"));
+
+ ASSERT_EQ(ssv.toBSON(),
+ BSON("setShardVersion"
+ << "db.coll"
+ << "init" << false << "configdb" << configCS.toString() << "shard"
+ << "TestShard"
+ << "shardHost" << shardCS.toString() << "version"
+ << Timestamp(chunkVersion.toLong()) << "versionEpoch" << chunkVersion.epoch()
+ << "authoritative" << false));
+}
+
+TEST(SetShardVersionRequest, ToSSVCommandFullAuthoritative) {
+ const ChunkVersion chunkVersion(1, 2, OID::gen());
+
+ SetShardVersionRequest ssv = SetShardVersionRequest::makeForVersioning(
+ configCS, "TestShard", shardCS, NamespaceString("db.coll"), chunkVersion, true);
+
+ ASSERT(!ssv.isInit());
+ ASSERT(ssv.isAuthoritative());
+ ASSERT_EQ(ssv.getConfigServer().toString(), configCS.toString());
+ ASSERT_EQ(ssv.getShardName(), "TestShard");
+ ASSERT_EQ(ssv.getShardConnectionString().toString(), shardCS.toString());
+ ASSERT_EQ(ssv.getNS().ns(), "db.coll");
+ ASSERT_EQ(ssv.getNSVersion().toBSONWithPrefix("version"),
+ chunkVersion.toBSONWithPrefix("version"));
+
+ ASSERT_EQ(ssv.toBSON(),
+ BSON("setShardVersion"
+ << "db.coll"
+ << "init" << false << "configdb" << configCS.toString() << "shard"
+ << "TestShard"
+ << "shardHost" << shardCS.toString() << "version"
+ << Timestamp(chunkVersion.toLong()) << "versionEpoch" << chunkVersion.epoch()
+ << "authoritative" << true));
+}
+
+} // namespace
+} // namespace mongo