summaryrefslogtreecommitdiff
path: root/src/mongo/rpc
diff options
context:
space:
mode:
authorDavid Storch <david.storch@mongodb.com>2021-10-01 13:24:06 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-10-01 14:08:31 +0000
commitfe4cf6134b16f102591053d6f4fe11e5cc0eb3ec (patch)
tree559991d99827a8b3252e2fdfb14b90a548de0793 /src/mongo/rpc
parent0950600446ee8030132bacc9c00eb4ebca411500 (diff)
downloadmongo-fe4cf6134b16f102591053d6f4fe11e5cc0eb3ec.tar.gz
SERVER-58210 Eliminate unnecessary RPC protocol negotiation code
Diffstat (limited to 'src/mongo/rpc')
-rw-r--r--src/mongo/rpc/SConscript12
-rw-r--r--src/mongo/rpc/factory.cpp1
-rw-r--r--src/mongo/rpc/metadata/sharding_metadata.h1
-rw-r--r--src/mongo/rpc/protocol.cpp209
-rw-r--r--src/mongo/rpc/protocol.h108
-rw-r--r--src/mongo/rpc/protocol_test.cpp337
6 files changed, 26 insertions, 642 deletions
diff --git a/src/mongo/rpc/SConscript b/src/mongo/rpc/SConscript
index d4cf4d5cac7..6901ff84661 100644
--- a/src/mongo/rpc/SConscript
+++ b/src/mongo/rpc/SConscript
@@ -29,12 +29,11 @@ if wiredtiger:
protoEnv.Library(
target=[
- 'protocol',
+ 'message',
],
source=[
'message.cpp',
'op_msg.cpp',
- 'protocol.cpp',
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
@@ -70,8 +69,8 @@ env.Library(
'$BUILD_DIR/mongo/idl/basic_types',
'$BUILD_DIR/mongo/s/common_s',
'$BUILD_DIR/mongo/util/net/network',
+ 'message',
'metadata',
- 'protocol',
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/idl/server_parameter',
@@ -89,9 +88,9 @@ env.Library(
'$BUILD_DIR/mongo/base',
'$BUILD_DIR/mongo/bson/mutable/mutable_bson',
'$BUILD_DIR/mongo/db/service_context',
- '$BUILD_DIR/mongo/rpc/protocol',
'$BUILD_DIR/mongo/s/is_mongos',
'$BUILD_DIR/third_party/shim_pcrecpp',
+ 'message',
],
)
@@ -103,7 +102,7 @@ env.CppLibfuzzerTest(
LIBDEPS=[
'$BUILD_DIR/mongo/db/ops/write_ops_parsers',
'$BUILD_DIR/mongo/transport/message_compressor',
- 'protocol',
+ 'message',
'rpc',
],
)
@@ -184,7 +183,6 @@ if wiredtiger:
'metadata_test.cpp',
'object_check_test.cpp',
'op_msg_test.cpp',
- 'protocol_test.cpp',
'reply_builder_test.cpp',
'rewrite_state_change_errors_test.cpp',
],
@@ -209,6 +207,6 @@ env.CppIntegrationTest(
'$BUILD_DIR/mongo/client/clientdriver_network',
'$BUILD_DIR/mongo/transport/transport_layer_egress_init',
'$BUILD_DIR/mongo/util/version_impl',
- 'protocol',
+ 'message',
],
)
diff --git a/src/mongo/rpc/factory.cpp b/src/mongo/rpc/factory.cpp
index 8211fb03d39..aa024ac2da8 100644
--- a/src/mongo/rpc/factory.cpp
+++ b/src/mongo/rpc/factory.cpp
@@ -38,7 +38,6 @@
#include "mongo/rpc/legacy_request.h"
#include "mongo/rpc/message.h"
#include "mongo/rpc/op_msg_rpc_impls.h"
-#include "mongo/rpc/protocol.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/str.h"
diff --git a/src/mongo/rpc/metadata/sharding_metadata.h b/src/mongo/rpc/metadata/sharding_metadata.h
index 1956ac4b7e3..5467707b722 100644
--- a/src/mongo/rpc/metadata/sharding_metadata.h
+++ b/src/mongo/rpc/metadata/sharding_metadata.h
@@ -30,7 +30,6 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/repl/optime.h"
-#include "mongo/rpc/protocol.h"
namespace mongo {
class BSONObj;
diff --git a/src/mongo/rpc/protocol.cpp b/src/mongo/rpc/protocol.cpp
deleted file mode 100644
index c021e79140a..00000000000
--- a/src/mongo/rpc/protocol.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * 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
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * 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 Server Side 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/rpc/protocol.h"
-
-#include <algorithm>
-#include <iterator>
-
-#include "mongo/base/string_data.h"
-#include "mongo/bson/util/bson_extract.h"
-#include "mongo/db/jsobj.h"
-#include "mongo/db/wire_version.h"
-#include "mongo/util/str.h"
-
-namespace mongo {
-namespace rpc {
-
-namespace {
-
-/**
- * Protocols supported by order of preference.
- */
-const Protocol kPreferredProtos[] = {Protocol::kOpMsg, Protocol::kOpQuery};
-
-struct ProtocolSetAndName {
- StringData name;
- ProtocolSet protocols;
-};
-
-constexpr ProtocolSetAndName protocolSetNames[] = {
- // Most common ones go first.
- {"all"_sd, supports::kAll}, // new mongod and mongos or very new client.
- {"opQueryOnly"_sd, supports::kOpQueryOnly}, // old mongos or mongod or moderately old client.
-
- // Then the rest (these should never happen in production).
- {"none"_sd, supports::kNone},
- {"opMsgOnly"_sd, supports::kOpMsgOnly},
-};
-
-} // namespace
-
-Protocol protocolForMessage(const Message& message) {
- switch (message.operation()) {
- case mongo::dbMsg:
- return Protocol::kOpMsg;
- case mongo::dbQuery:
- return Protocol::kOpQuery;
- default:
- uasserted(ErrorCodes::UnsupportedFormat,
- str::stream() << "Received a reply message with unexpected opcode: "
- << message.operation());
- }
-}
-
-StatusWith<Protocol> negotiate(ProtocolSet fst, ProtocolSet snd) {
- using std::begin;
- using std::end;
-
- ProtocolSet common = fst & snd;
-
- auto it = std::find_if(begin(kPreferredProtos), end(kPreferredProtos), [common](Protocol p) {
- return common & static_cast<ProtocolSet>(p);
- });
-
- if (it == end(kPreferredProtos)) {
- return Status(ErrorCodes::RPCProtocolNegotiationFailed, "No common protocol found.");
- }
- return *it;
-}
-
-StatusWith<StringData> toString(ProtocolSet protocols) {
- for (auto& elem : protocolSetNames) {
- if (elem.protocols == protocols)
- return elem.name;
- }
- return Status(ErrorCodes::BadValue,
- str::stream() << "ProtocolSet " << protocols
- << " does not match any well-known value.");
-}
-
-StatusWith<ProtocolSet> parseProtocolSet(StringData name) {
- for (auto& elem : protocolSetNames) {
- if (elem.name == name)
- return elem.protocols;
- }
- return Status(ErrorCodes::BadValue,
- str::stream() << name << " is not a valid name for a ProtocolSet.");
-}
-
-StatusWith<ProtocolSetAndWireVersionInfo> parseProtocolSetFromIsMasterReply(
- const BSONObj& isMasterReply) {
- long long maxWireVersion;
- auto maxWireExtractStatus =
- bsonExtractIntegerField(isMasterReply, "maxWireVersion", &maxWireVersion);
-
- long long minWireVersion;
- auto minWireExtractStatus =
- bsonExtractIntegerField(isMasterReply, "minWireVersion", &minWireVersion);
-
- // MongoDB 2.4 and earlier do not have maxWireVersion/minWireVersion in their 'isMaster' replies
- if ((maxWireExtractStatus == minWireExtractStatus) &&
- (maxWireExtractStatus == ErrorCodes::NoSuchKey)) {
- return {{supports::kOpQueryOnly, {0, 0}}};
- } else if (!maxWireExtractStatus.isOK()) {
- return maxWireExtractStatus;
- } else if (!minWireExtractStatus.isOK()) {
- return minWireExtractStatus;
- }
-
- if (minWireVersion < 0 || maxWireVersion < 0 ||
- minWireVersion >= std::numeric_limits<int>::max() ||
- maxWireVersion >= std::numeric_limits<int>::max()) {
- return Status(ErrorCodes::IncompatibleServerVersion,
- str::stream() << "Server min and max wire version have invalid values ("
- << minWireVersion << "," << maxWireVersion << ")");
- }
-
- WireVersionInfo version{static_cast<int>(minWireVersion), static_cast<int>(maxWireVersion)};
-
- auto protos = computeProtocolSet(version);
- return {{protos, version}};
-}
-
-ProtocolSet computeProtocolSet(const WireVersionInfo version) {
- ProtocolSet result = supports::kNone;
- if (version.minWireVersion <= version.maxWireVersion) {
- if (version.maxWireVersion >= WireVersion::SUPPORTS_OP_MSG) {
- result |= supports::kOpMsgOnly;
- }
- if (version.minWireVersion <= WireVersion::RELEASE_2_4_AND_BEFORE) {
- result |= supports::kOpQueryOnly;
- }
- // Note: this means anything using the internal handshake cannot talk to servers between 2.6
- // and 3.6, since the servers will reply with higher minWireVersions. The shell should still
- // be able to connect to those versions but will just use OP_QUERY to run commands.
- }
- return result;
-}
-
-Status validateWireVersion(const WireVersionInfo client, const WireVersionInfo server) {
- // Since this is defined in the code, it should always hold true since this is the versions that
- // mongos/d wants to connect to.
- invariant(client.minWireVersion <= client.maxWireVersion);
-
- // Server may return bad data.
- if (server.minWireVersion > server.maxWireVersion) {
- return Status(ErrorCodes::IncompatibleServerVersion,
- str::stream()
- << "Server min and max wire version are incorrect ("
- << server.minWireVersion << "," << server.maxWireVersion << ")");
- }
-
- // Determine if the [min, max] tuples overlap.
- // We assert the invariant that min < max above.
- if (!(client.minWireVersion <= server.maxWireVersion &&
- client.maxWireVersion >= server.minWireVersion)) {
- std::string errmsg = str::stream()
- << "Server min and max wire version (" << server.minWireVersion << ","
- << server.maxWireVersion << ") is incompatible with client min wire version ("
- << client.minWireVersion << "," << client.maxWireVersion << ").";
- if (client.maxWireVersion < server.minWireVersion) {
- return Status(ErrorCodes::IncompatibleWithUpgradedServer,
- str::stream()
- << errmsg
- << "You (client) are attempting to connect to a node (server) that "
- "no longer accepts connections with your (client’s) binary "
- "version. Please upgrade the client’s binary version.");
- }
- return Status(ErrorCodes::IncompatibleServerVersion,
- str::stream() << errmsg
- << "You (client) are attempting to connect to a node "
- "(server) with a binary version with which "
- "you (client) no longer accept connections. Please "
- "upgrade the server’s binary version.");
- }
-
- return Status::OK();
-}
-
-} // namespace rpc
-} // namespace mongo
diff --git a/src/mongo/rpc/protocol.h b/src/mongo/rpc/protocol.h
index 1f0a966e1f2..ea95bc4d844 100644
--- a/src/mongo/rpc/protocol.h
+++ b/src/mongo/rpc/protocol.h
@@ -29,31 +29,26 @@
#pragma once
-#include <cstdint>
-#include <string>
-#include <type_traits>
+#include <fmt/format.h>
-#include "mongo/base/status_with.h"
-#include "mongo/db/wire_version.h"
#include "mongo/rpc/message.h"
+#include "mongo/util/assert_util.h"
namespace mongo {
-class BSONObj;
-class OperationContext;
namespace rpc {
/**
- * Bit flags representing support for a particular RPC protocol.
- * This is just an internal representation, and is never transmitted over the wire. It should
- * never be used for any other feature detection in favor of max/min wire version.
+ * Bit flags representing support for a particular RPC protocol. This is just an internal
+ * representation, and is never transmitted over the wire. It should never be used for any other
+ * feature detection in favor of max/min wire version.
*
- * A new protocol must be added as the highest order bit flag so that it is prioritized in
- * negotiation.
+ * The system only currently offers full support for the OP_MSG protocol. However, it can continue
+ * to handle OP_QUERY in some limited cases, in particular for the hello/isMaster command sent by
+ * clients on connection open.
*/
enum class Protocol : std::uint64_t {
-
/**
- * The pre-3.2 OP_QUERY on db.$cmd protocol
+ * The pre-3.6 OP_QUERY on db.$cmd protocol
*/
kOpQuery = 1 << 0,
@@ -63,79 +58,18 @@ enum class Protocol : std::uint64_t {
kOpMsg = 1 << 1,
};
-/**
- * Bitfield representing a set of supported RPC protocols.
- */
-using ProtocolSet = std::underlying_type<Protocol>::type;
-
-/**
- * This namespace contains predefined bitfields for common levels of protocol support.
- */
-namespace supports {
-
-const ProtocolSet kNone = ProtocolSet{0};
-const ProtocolSet kOpQueryOnly = static_cast<ProtocolSet>(Protocol::kOpQuery);
-const ProtocolSet kOpMsgOnly = static_cast<ProtocolSet>(Protocol::kOpMsg);
-const ProtocolSet kAll = kOpQueryOnly | kOpMsgOnly;
-
-} // namespace supports
-
-Protocol protocolForMessage(const Message& message);
-
-/**
- * Returns the protocol used to initiate the current operation.
- */
-Protocol getOperationProtocol(OperationContext* opCtx);
-
-/**
- * Sets the protocol used to initiate the current operation.
- */
-void setOperationProtocol(OperationContext* opCtx, Protocol protocol);
-
-/**
- * Returns the newest protocol supported by two parties.
- */
-StatusWith<Protocol> negotiate(ProtocolSet fst, ProtocolSet snd);
-
-/**
- * Converts a ProtocolSet to a string. Currently only the predefined ProtocolSets in the
- * 'supports' namespace are supported.
- *
- * This intentionally does not conform to the STL 'to_string' convention so that it will
- * not conflict with the to_string overload for uint64_t.
- */
-StatusWith<StringData> toString(ProtocolSet protocols);
-
-/**
- * Parses a ProtocolSet from a string. Currently only the predefined ProtocolSets in the
- * 'supports' namespace are supported
- */
-StatusWith<ProtocolSet> parseProtocolSet(StringData repr);
-
-/**
- * Validates client and server wire version. The server is returned from isMaster, and the client is
- * from WireSpec.instance().
- */
-Status validateWireVersion(WireVersionInfo client, WireVersionInfo server);
-
-/**
- * Struct to pass around information about protocol set and wire version.
- */
-struct ProtocolSetAndWireVersionInfo {
- ProtocolSet protocolSet;
- WireVersionInfo version;
-};
-
-/**
- * Determines the ProtocolSet of a remote server from an isMaster reply.
- */
-StatusWith<ProtocolSetAndWireVersionInfo> parseProtocolSetFromIsMasterReply(
- const BSONObj& isMasterReply);
-
-/**
- * Computes supported protocols from wire versions.
- */
-ProtocolSet computeProtocolSet(WireVersionInfo version);
+inline Protocol protocolForMessage(const Message& message) {
+ switch (message.operation()) {
+ case mongo::dbMsg:
+ return Protocol::kOpMsg;
+ case mongo::dbQuery:
+ return Protocol::kOpQuery;
+ default:
+ uasserted(ErrorCodes::UnsupportedFormat,
+ fmt::format("Received a reply message with unexpected opcode: {}",
+ message.operation()));
+ }
+}
} // namespace rpc
} // namespace mongo
diff --git a/src/mongo/rpc/protocol_test.cpp b/src/mongo/rpc/protocol_test.cpp
deleted file mode 100644
index 97f6ac1d763..00000000000
--- a/src/mongo/rpc/protocol_test.cpp
+++ /dev/null
@@ -1,337 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * 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
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * 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 Server Side 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/base/status.h"
-#include "mongo/db/jsobj.h"
-#include "mongo/db/wire_version.h"
-#include "mongo/rpc/protocol.h"
-#include "mongo/unittest/unittest.h"
-
-#include <vector>
-
-namespace {
-
-using mongo::WireVersion;
-using mongo::WireVersionInfo;
-using namespace mongo::rpc;
-using mongo::BSONObj;
-using mongo::unittest::assertGet;
-
-using std::vector;
-
-// Checks if negotiation of the first to protocol sets results in the 'proto'
-const auto assert_negotiated = [](ProtocolSet fst, ProtocolSet snd, Protocol proto) {
- auto negotiated = negotiate(fst, snd);
- ASSERT_TRUE(negotiated.isOK());
- ASSERT_TRUE(negotiated.getValue() == proto);
-};
-
-TEST(Protocol, SuccessfulNegotiation) {
- assert_negotiated(supports::kAll, supports::kAll, Protocol::kOpMsg);
- assert_negotiated(supports::kAll, supports::kOpMsgOnly, Protocol::kOpMsg);
- assert_negotiated(supports::kAll, supports::kOpQueryOnly, Protocol::kOpQuery);
-}
-
-// Checks that negotiation fails
-const auto assert_not_negotiated = [](ProtocolSet fst, ProtocolSet snd) {
- auto proto = negotiate(fst, snd);
- ASSERT_TRUE(!proto.isOK());
- ASSERT_TRUE(proto.getStatus().code() == mongo::ErrorCodes::RPCProtocolNegotiationFailed);
-};
-
-TEST(Protocol, FailedNegotiation) {
- assert_not_negotiated(supports::kOpQueryOnly, supports::kOpMsgOnly);
- assert_not_negotiated(supports::kAll, supports::kNone);
- assert_not_negotiated(supports::kOpQueryOnly, supports::kNone);
- assert_not_negotiated(supports::kOpMsgOnly, supports::kNone);
-}
-
-/*
- * Tests the following:
- * - Replies from MongoDB 2.4 and older reply with supports::kOpQueryOnly
- * - Replies from versions of MongoDB older than 3.6 returns supports::kOpQueryOnly
- * - Replies from MongoDB 3.6 reply with supports::kAll
- * - Replies from latest, last-continuous, and last-lts versions of MongoDB returns supports::kAll
- */
-TEST(Protocol, parseProtocolSetFromIsMasterReply) {
- {
- // latest version of MongoDB (mongod)
- auto latestMongod =
- BSON("maxWireVersion" << static_cast<int>(WireVersion::LATEST_WIRE_VERSION)
- << "minWireVersion"
- << static_cast<int>(WireVersion::RELEASE_2_4_AND_BEFORE));
-
- ASSERT_EQ(assertGet(parseProtocolSetFromIsMasterReply(latestMongod)).protocolSet,
- supports::kAll);
- }
- {
- // last continuous version of MongoDB (mongod)
- auto lastContMongod =
- BSON("maxWireVersion" << static_cast<int>(WireVersion::LAST_CONT_WIRE_VERSION)
- << "minWireVersion"
- << static_cast<int>(WireVersion::RELEASE_2_4_AND_BEFORE));
-
- ASSERT_EQ(assertGet(parseProtocolSetFromIsMasterReply(lastContMongod)).protocolSet,
- supports::kAll);
- }
- {
- // last LTS version of MongoDB (mongod)
- auto lastLtsMongod =
- BSON("maxWireVersion" << static_cast<int>(WireVersion::LAST_LTS_WIRE_VERSION)
- << "minWireVersion"
- << static_cast<int>(WireVersion::RELEASE_2_4_AND_BEFORE));
-
- ASSERT_EQ(assertGet(parseProtocolSetFromIsMasterReply(lastLtsMongod)).protocolSet,
- supports::kAll);
- }
- {
- // MongoDB 3.6
- auto mongod36 =
- BSON("maxWireVersion" << static_cast<int>(WireVersion::SUPPORTS_OP_MSG) //
- << "minWireVersion"
- << static_cast<int>(WireVersion::RELEASE_2_4_AND_BEFORE));
-
- ASSERT_EQ(assertGet(parseProtocolSetFromIsMasterReply(mongod36)).protocolSet,
- supports::kAll);
- }
- {
- // MongoDB 3.2 (mongod)
- auto mongod32 =
- BSON("maxWireVersion" << static_cast<int>(WireVersion::COMMANDS_ACCEPT_WRITE_CONCERN)
- << "minWireVersion"
- << static_cast<int>(WireVersion::RELEASE_2_4_AND_BEFORE));
-
- ASSERT_EQ(assertGet(parseProtocolSetFromIsMasterReply(mongod32)).protocolSet,
- supports::kOpQueryOnly); // This used to also include OP_COMMAND.
- }
- {
- // MongoDB 3.2 (mongos)
- auto mongos32 =
- BSON("maxWireVersion" << static_cast<int>(WireVersion::COMMANDS_ACCEPT_WRITE_CONCERN)
- << "minWireVersion"
- << static_cast<int>(WireVersion::RELEASE_2_4_AND_BEFORE) << "msg"
- << "isdbgrid");
-
- ASSERT_EQ(assertGet(parseProtocolSetFromIsMasterReply(mongos32)).protocolSet,
- supports::kOpQueryOnly);
- }
- {
- // MongoDB 2.4 and earlier do not have maxWireVersion/minWireVersion in their 'isMaster'
- // replies.
- auto mongod24 = BSONObj();
- ASSERT_EQ(assertGet(parseProtocolSetFromIsMasterReply(mongod24)).protocolSet,
- supports::kOpQueryOnly);
- }
-}
-
-#define VALIDATE_WIRE_VERSION(macro, clientMin, clientMax, serverMin, serverMax) \
- do { \
- auto msg = BSON("minWireVersion" << static_cast<int>(serverMin) << "maxWireVersion" \
- << static_cast<int>(serverMax)); \
- auto swReply = parseProtocolSetFromIsMasterReply(msg); \
- ASSERT_OK(swReply.getStatus()); \
- macro(validateWireVersion({clientMin, clientMax}, swReply.getValue().version)); \
- } while (0);
-
-TEST(Protocol, validateWireVersion) {
- // Min, max FCV version pairs representing valid WireVersion ranges for variable binary
- // versions used to communicate with the MongoD 'latest' binary version.
-
- // MongoD 'latest' binary
- vector<WireVersionInfo> mongoDLatestBinaryRanges = {
- // upgraded FCV
- {WireVersion::LATEST_WIRE_VERSION, WireVersion::LATEST_WIRE_VERSION},
- // downgraded 'last-cont' FCV
- {WireVersion::LAST_CONT_WIRE_VERSION, WireVersion::LATEST_WIRE_VERSION},
- // downgraded 'last-lts' FCV
- {WireVersion::LAST_LTS_WIRE_VERSION, WireVersion::LATEST_WIRE_VERSION}};
-
- // MongoS binary versions
- vector<WireVersionInfo> mongoSBinaryRanges = {
- // 'latest' binary
- {WireVersion::LATEST_WIRE_VERSION, WireVersion::LATEST_WIRE_VERSION},
- // 'last-cont' binary
- {WireVersion::LAST_CONT_WIRE_VERSION, WireVersion::LAST_CONT_WIRE_VERSION},
- // 'last-lts' binary
- {WireVersion::LAST_LTS_WIRE_VERSION, WireVersion::LAST_LTS_WIRE_VERSION}};
-
- /*
- * Test communication between:
- * MongoD 'latest' binary version <-> MongoD 'latest' binary version
- * 'latest' should always be able to communicate to 'latest' regardless of FCV.
- */
-
- for (const auto& clientRange : mongoDLatestBinaryRanges) {
- for (const auto& serverRange : mongoDLatestBinaryRanges) {
- // We don't need to test when FCV != 'latest' && client FCV != server FCV because we
- // don't expect users to be in this state when following our recommended
- // upgrade/downgrade procedure.
- if (clientRange.minWireVersion < WireVersion::LATEST_WIRE_VERSION &&
- serverRange.minWireVersion < WireVersion::LATEST_WIRE_VERSION &&
- clientRange.minWireVersion != serverRange.minWireVersion) {
- continue;
- }
- VALIDATE_WIRE_VERSION(ASSERT_OK,
- clientRange.minWireVersion,
- clientRange.maxWireVersion,
- serverRange.minWireVersion,
- serverRange.maxWireVersion);
- }
- }
-
- /*
- * Test communication between:
- * MongoD 'latest' binary version <-> 'last-cont' binary version with 'last-cont' FCV
- */
-
- // 'latest' binary with 'latest' FCV -> 'last-cont' binary with 'last-cont' FCV
- VALIDATE_WIRE_VERSION(ASSERT_NOT_OK,
- WireVersion::LATEST_WIRE_VERSION,
- WireVersion::LATEST_WIRE_VERSION,
- WireVersion::LAST_CONT_WIRE_VERSION,
- WireVersion::LAST_CONT_WIRE_VERSION);
-
- // 'latest' binary with 'last-cont' FCV -> 'last-cont' binary with 'last-cont' FCV
- VALIDATE_WIRE_VERSION(ASSERT_OK,
- WireVersion::LAST_CONT_WIRE_VERSION,
- WireVersion::LATEST_WIRE_VERSION,
- WireVersion::LAST_CONT_WIRE_VERSION,
- WireVersion::LAST_CONT_WIRE_VERSION);
-
- // 'last-cont' binary with 'last-cont' FCV -> 'latest' binary with 'latest' FCV
- VALIDATE_WIRE_VERSION(ASSERT_NOT_OK,
- WireVersion::LAST_CONT_WIRE_VERSION,
- WireVersion::LAST_CONT_WIRE_VERSION,
- WireVersion::LATEST_WIRE_VERSION,
- WireVersion::LATEST_WIRE_VERSION);
-
- // 'last-cont' binary with 'last-cont' FCV -> 'latest' binary with 'last-cont' FCV
- VALIDATE_WIRE_VERSION(ASSERT_OK,
- WireVersion::LAST_CONT_WIRE_VERSION,
- WireVersion::LAST_CONT_WIRE_VERSION,
- WireVersion::LAST_CONT_WIRE_VERSION,
- WireVersion::LATEST_WIRE_VERSION);
-
- /*
- * Test communication between:
- * MongoD 'latest' binary version <-> 'last-lts' binary version with 'last-lts' FCV
- */
-
- // 'latest' binary with 'latest' FCV -> 'last-lts' binary with 'last-lts' FCV
- VALIDATE_WIRE_VERSION(ASSERT_NOT_OK,
- WireVersion::LATEST_WIRE_VERSION,
- WireVersion::LATEST_WIRE_VERSION,
- WireVersion::LAST_LTS_WIRE_VERSION,
- WireVersion::LAST_LTS_WIRE_VERSION);
-
- // 'latest' binary with 'last-cont' FCV -> 'last-lts' binary with 'last-lts' FCV
- if (WireVersion::LAST_CONT_WIRE_VERSION != WireVersion::LAST_LTS_WIRE_VERSION) {
- VALIDATE_WIRE_VERSION(ASSERT_NOT_OK,
- WireVersion::LAST_CONT_WIRE_VERSION,
- WireVersion::LATEST_WIRE_VERSION,
- WireVersion::LAST_LTS_WIRE_VERSION,
- WireVersion::LAST_LTS_WIRE_VERSION);
- }
-
- // 'latest' binary with 'last-lts' FCV -> 'last-lts' binary with 'last-lts' FCV
- VALIDATE_WIRE_VERSION(ASSERT_OK,
- WireVersion::LAST_LTS_WIRE_VERSION,
- WireVersion::LATEST_WIRE_VERSION,
- WireVersion::LAST_CONT_WIRE_VERSION,
- WireVersion::LAST_CONT_WIRE_VERSION);
-
- // 'last-lts' binary with 'last-lts' FCV -> 'latest' binary with 'latest' FCV
- VALIDATE_WIRE_VERSION(ASSERT_NOT_OK,
- WireVersion::LAST_LTS_WIRE_VERSION,
- WireVersion::LAST_LTS_WIRE_VERSION,
- WireVersion::LATEST_WIRE_VERSION,
- WireVersion::LATEST_WIRE_VERSION);
-
- // 'last-lts' binary with 'last-lts' FCV -> 'latest' binary with 'last-lts' FCV
- VALIDATE_WIRE_VERSION(ASSERT_OK,
- WireVersion::LAST_LTS_WIRE_VERSION,
- WireVersion::LAST_LTS_WIRE_VERSION,
- WireVersion::LAST_LTS_WIRE_VERSION,
- WireVersion::LATEST_WIRE_VERSION);
-
- /*
- * Test communication between:
- * variable MongoS binary version -> MongoD 'latest' binary version
- * Note that it is disallowed for MongoS to communicate with a lower binary server.
- */
-
- for (const auto& mongoSRange : mongoSBinaryRanges) {
- for (const auto& mongoDLatestRange : mongoDLatestBinaryRanges) {
- // MongoS 'latest' binary can communicate with all FCV versions.
- // MongoS 'last-cont' binary can only communicate with MongoD downgraded 'last-cont' and
- // 'last-lts' FCV.
- // MongoS 'last-lts' binary can only communicate with MongoD downgraded 'last-lts' FCV.
- if (mongoSRange.minWireVersion == WireVersion::LATEST_WIRE_VERSION ||
- (mongoSRange.minWireVersion == WireVersion::LAST_CONT_WIRE_VERSION &&
- mongoDLatestRange.minWireVersion == WireVersion::LAST_CONT_WIRE_VERSION) ||
- (mongoSRange.minWireVersion == WireVersion::LAST_LTS_WIRE_VERSION &&
- mongoDLatestRange.minWireVersion == WireVersion::LAST_LTS_WIRE_VERSION) ||
- (mongoSRange.minWireVersion == WireVersion::LAST_CONT_WIRE_VERSION &&
- mongoDLatestRange.minWireVersion == WireVersion::LAST_LTS_WIRE_VERSION)) {
- VALIDATE_WIRE_VERSION(ASSERT_OK,
- mongoSRange.minWireVersion,
- mongoSRange.maxWireVersion,
- mongoDLatestRange.minWireVersion,
- mongoDLatestRange.maxWireVersion);
- } else {
- VALIDATE_WIRE_VERSION(ASSERT_NOT_OK,
- mongoSRange.minWireVersion,
- mongoSRange.maxWireVersion,
- mongoDLatestRange.minWireVersion,
- mongoDLatestRange.maxWireVersion);
- }
- }
- }
-}
-
-// A mongos is unable to communicate with a fully upgraded cluster with a higher wire version.
-TEST(Protocol, validateWireVersionFailsForUpgradedServerNode) {
- // Server is fully upgraded to the latest wire version.
- auto msg = BSON("minWireVersion" << static_cast<int>(WireVersion::LATEST_WIRE_VERSION)
- << "maxWireVersion"
- << static_cast<int>(WireVersion::LATEST_WIRE_VERSION));
- auto swReply = parseProtocolSetFromIsMasterReply(msg);
- ASSERT_OK(swReply.getStatus());
-
- // The client (this mongos server) only has the previous wire version.
- ASSERT_EQUALS(mongo::ErrorCodes::IncompatibleWithUpgradedServer,
- validateWireVersion(
- {WireVersion::LATEST_WIRE_VERSION - 1, WireVersion::LATEST_WIRE_VERSION - 1},
- swReply.getValue().version)
- .code());
-}
-
-} // namespace