diff options
author | David Storch <david.storch@mongodb.com> | 2021-10-01 13:24:06 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-10-01 14:08:31 +0000 |
commit | fe4cf6134b16f102591053d6f4fe11e5cc0eb3ec (patch) | |
tree | 559991d99827a8b3252e2fdfb14b90a548de0793 /src/mongo/rpc | |
parent | 0950600446ee8030132bacc9c00eb4ebca411500 (diff) | |
download | mongo-fe4cf6134b16f102591053d6f4fe11e5cc0eb3ec.tar.gz |
SERVER-58210 Eliminate unnecessary RPC protocol negotiation code
Diffstat (limited to 'src/mongo/rpc')
-rw-r--r-- | src/mongo/rpc/SConscript | 12 | ||||
-rw-r--r-- | src/mongo/rpc/factory.cpp | 1 | ||||
-rw-r--r-- | src/mongo/rpc/metadata/sharding_metadata.h | 1 | ||||
-rw-r--r-- | src/mongo/rpc/protocol.cpp | 209 | ||||
-rw-r--r-- | src/mongo/rpc/protocol.h | 108 | ||||
-rw-r--r-- | src/mongo/rpc/protocol_test.cpp | 337 |
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 |