diff options
author | Adityavardhan Agrawal <aa729@cornell.edu> | 2022-09-19 18:10:07 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-09-19 19:11:26 +0000 |
commit | 14794d91aa3d1aa618de891f99abe0fa9f603a21 (patch) | |
tree | a2cc5c5b07ad3db8ecb9bb962280b8e9ef6e5496 /src/mongo | |
parent | 8356c555ca119bf08b6c476c5a119c2a3364ec89 (diff) | |
download | mongo-14794d91aa3d1aa618de891f99abe0fa9f603a21.tar.gz |
SERVER-67155: serialize and deserialize namespaceString correctly
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/catalog/collection_catalog.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/catalog/views_for_database.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/commands.h | 2 | ||||
-rw-r--r-- | src/mongo/db/repl/oplog_entry.idl | 2 | ||||
-rw-r--r-- | src/mongo/db/repl/tenant_oplog_applier_test.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/views/durable_view_catalog.cpp | 8 | ||||
-rw-r--r-- | src/mongo/util/SConscript | 24 | ||||
-rw-r--r-- | src/mongo/util/namespace_string_util.cpp | 68 | ||||
-rw-r--r-- | src/mongo/util/namespace_string_util.h | 77 | ||||
-rw-r--r-- | src/mongo/util/namespace_string_util_test.cpp | 152 |
10 files changed, 331 insertions, 10 deletions
diff --git a/src/mongo/db/catalog/collection_catalog.cpp b/src/mongo/db/catalog/collection_catalog.cpp index 7a98aae38b2..dca8d9dfc2b 100644 --- a/src/mongo/db/catalog/collection_catalog.cpp +++ b/src/mongo/db/catalog/collection_catalog.cpp @@ -1593,7 +1593,7 @@ Status CollectionCatalog::_createOrUpdateView( // Build the BSON definition for this view to be saved in the durable view catalog and/or to // insert in the viewMap. If the collation is empty, omit it from the definition altogether. BSONObjBuilder viewDefBuilder; - // TODO SERVER-67155 Use serialize function on NamespaceString to create the string to write. + // TODO SERVER-69499 Use serialize function on NamespaceString to create the string to write. if (!gMultitenancySupport || (serverGlobalParams.featureCompatibility.isVersionInitialized() && gFeatureFlagRequireTenantID.isEnabled(serverGlobalParams.featureCompatibility))) { diff --git a/src/mongo/db/catalog/views_for_database.cpp b/src/mongo/db/catalog/views_for_database.cpp index 5fd510fff55..2ebf6c18b91 100644 --- a/src/mongo/db/catalog/views_for_database.cpp +++ b/src/mongo/db/catalog/views_for_database.cpp @@ -107,7 +107,7 @@ Status ViewsForDatabase::_insert(OperationContext* opCtx, } NamespaceString viewName; - // TODO SERVER-67155 Use deserialize function on NamespaceString to reconstruct NamespaceString + // TODO SERVER-69499 Use deserialize function on NamespaceString to reconstruct NamespaceString // correctly. if (!gMultitenancySupport || (serverGlobalParams.featureCompatibility.isVersionInitialized() && diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h index d903da77372..64a83ee99ae 100644 --- a/src/mongo/db/commands.h +++ b/src/mongo/db/commands.h @@ -1154,7 +1154,7 @@ private: static RequestType _parseRequest(OperationContext* opCtx, const DatabaseName& dbName, const BSONObj& cmdObj) { - // TODO SERVER-67155 pass tenantId to the BSONObj parse function + // TODO SERVER-69499 pass tenantId to the BSONObj parse function return RequestType::parse( IDLParserContext(RequestType::kCommandName, APIParameters::get(opCtx).getAPIStrict().value_or(false)), diff --git a/src/mongo/db/repl/oplog_entry.idl b/src/mongo/db/repl/oplog_entry.idl index ed433979f7b..af75666591d 100644 --- a/src/mongo/db/repl/oplog_entry.idl +++ b/src/mongo/db/repl/oplog_entry.idl @@ -60,7 +60,7 @@ enums: kPostImage: "postImage" structs: - # TODO SERVER-67155 Ensure the tenantId is included in the serialized "ns" field when + # TODO SERVER-69499 Ensure the tenantId is included in the serialized "ns" field when # multitenancySupport is on but featureFlagRequireTenantId is off. Currently it will not be # included in either place DurableReplOperation: diff --git a/src/mongo/db/repl/tenant_oplog_applier_test.cpp b/src/mongo/db/repl/tenant_oplog_applier_test.cpp index 9905fc65c33..495ec4b3c79 100644 --- a/src/mongo/db/repl/tenant_oplog_applier_test.cpp +++ b/src/mongo/db/repl/tenant_oplog_applier_test.cpp @@ -205,7 +205,7 @@ private: logv2::LogComponent::kTenantMigration, logv2::LogSeverity::Debug(1)}; }; -// TODO SERVER-67155 Remove all calls to DatabaseName::toStringWithTenantId() once the OplogEntry +// TODO SERVER-69499 Remove all calls to DatabaseName::toStringWithTenantId() once the OplogEntry // deserializer passes "tid" to the NamespaceString constructor TEST_F(TenantOplogApplierTest, NoOpsForSingleBatch) { std::vector<OplogEntry> srcOps; @@ -847,7 +847,7 @@ TEST_F(TenantOplogApplierTest, ApplyDelete_Success) { ASSERT_TRUE(opCtx->lockState()->isCollectionLockedForMode(nss, MODE_IX)); ASSERT_TRUE(opCtx->writesAreReplicated()); ASSERT_FALSE(args.fromMigrate); - // TODO SERVER-67155 Check that (nss.dbName() == _dbName) once the OplogEntry deserializer + // TODO SERVER-69499 Check that (nss.dbName() == _dbName) once the OplogEntry deserializer // passes "tid" to the NamespaceString constructor ASSERT_EQUALS(nss.dbName().db(), _dbName.toStringWithTenantId()); ASSERT_EQUALS(nss.coll(), "bar"); diff --git a/src/mongo/db/views/durable_view_catalog.cpp b/src/mongo/db/views/durable_view_catalog.cpp index 748ee8bbc49..603de20e69f 100644 --- a/src/mongo/db/views/durable_view_catalog.cpp +++ b/src/mongo/db/views/durable_view_catalog.cpp @@ -72,7 +72,7 @@ void validateViewDefinitionBSON(OperationContext* opCtx, } NamespaceString viewName; - // TODO SERVER-67155 Use deserialize function on NamespaceString to reconstruct NamespaceString + // TODO SERVER-69499 Use deserialize function on NamespaceString to reconstruct NamespaceString // correctly. if (!gMultitenancySupport || (serverGlobalParams.featureCompatibility.isVersionInitialized() && @@ -143,7 +143,7 @@ Status DurableViewCatalog::onExternalInsert(OperationContext* opCtx, auto catalog = CollectionCatalog::get(opCtx); NamespaceString viewName; - // TODO SERVER-67155 Use deserialize function on NamespaceString to reconstruct NamespaceString + // TODO SERVER-69499 Use deserialize function on NamespaceString to reconstruct NamespaceString // correctly. if (!gMultitenancySupport || (serverGlobalParams.featureCompatibility.isVersionInitialized() && @@ -260,7 +260,7 @@ void DurableViewCatalogImpl::upsert(OperationContext* opCtx, invariant(systemViews); std::string nssOnDisk; - // TODO SERVER-67155 Move this check into a function on NamespaceString. + // TODO SERVER-69499 Move this check into a function on NamespaceString. if (!gMultitenancySupport || (serverGlobalParams.featureCompatibility.isVersionInitialized() && gFeatureFlagRequireTenantID.isEnabled(serverGlobalParams.featureCompatibility))) { @@ -304,7 +304,7 @@ void DurableViewCatalogImpl::remove(OperationContext* opCtx, const NamespaceStri std::string nssOnDisk; - // TODO SERVER-67155 Move this check into a function on NamespaceString. + // TODO SERVER-69499 Move this check into a function on NamespaceString. if (!gMultitenancySupport || (serverGlobalParams.featureCompatibility.isVersionInitialized() && gFeatureFlagRequireTenantID.isEnabled(serverGlobalParams.featureCompatibility))) { diff --git a/src/mongo/util/SConscript b/src/mongo/util/SConscript index fc59a929ee0..e0b0487dd49 100644 --- a/src/mongo/util/SConscript +++ b/src/mongo/util/SConscript @@ -228,6 +228,30 @@ env.Library( ) env.Library( + target='namespace_string_util', + source=[ + 'namespace_string_util.cpp', + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/db/multitenancy', + '$BUILD_DIR/mongo/db/server_base', + '$BUILD_DIR/mongo/db/server_feature_flags', + '$BUILD_DIR/mongo/db/server_options_core', + '$BUILD_DIR/mongo/idl/feature_flag', + ], +) + +env.CppUnitTest( + target='namespace_string_util_test', + source=[ + 'namespace_string_util_test.cpp', + ], + LIBDEPS=[ + 'namespace_string_util', + ], +) + +env.Library( target="periodic_runner", source=[ "periodic_runner.cpp", diff --git a/src/mongo/util/namespace_string_util.cpp b/src/mongo/util/namespace_string_util.cpp new file mode 100644 index 00000000000..141d5781ec8 --- /dev/null +++ b/src/mongo/util/namespace_string_util.cpp @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2022-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/util/namespace_string_util.h" +#include "mongo/db/multitenancy_gen.h" +#include "mongo/db/namespace_string.h" +#include "mongo/db/server_feature_flags_gen.h" +#include "mongo/util/str.h" +#include <ostream> + +namespace mongo { + +std::string NamespaceStringUtil::serialize(const NamespaceString& ns) { + if (gMultitenancySupport) { + if (serverGlobalParams.featureCompatibility.isVersionInitialized() && + gFeatureFlagRequireTenantID.isEnabled(serverGlobalParams.featureCompatibility)) { + return ns.toString(); + } + return ns.toStringWithTenantId(); + } + return ns.toString(); +} + +NamespaceString NamespaceStringUtil::deserialize(boost::optional<TenantId> tenantId, + StringData ns) { + if (gMultitenancySupport) { + if (serverGlobalParams.featureCompatibility.isVersionInitialized() && + gFeatureFlagRequireTenantID.isEnabled(serverGlobalParams.featureCompatibility)) { + invariant(tenantId != boost::none); + return NamespaceString(std::move(tenantId), ns); + } + auto nss = NamespaceString::parseFromStringExpectTenantIdInMultitenancyMode(ns); + if (tenantId != boost::none) { + invariant(tenantId == nss.tenantId()); + } + return nss; + } + invariant(tenantId == boost::none); + return NamespaceString(boost::none, ns); +} + +} // namespace mongo diff --git a/src/mongo/util/namespace_string_util.h b/src/mongo/util/namespace_string_util.h new file mode 100644 index 00000000000..001c0f785c0 --- /dev/null +++ b/src/mongo/util/namespace_string_util.h @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2022-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. + */ + +#pragma once + +#include "mongo/db/namespace_string.h" +#include "mongo/db/tenant_id.h" + +namespace mongo { + +class NamespaceStringUtil { +public: + /** + * Serializes a NamespaceString object. + * + * If multitenancySupport is enabled and featureFlagRequireTenantID is enabled, then tenantId is + * not included in the serialization. + * eg. serialize(NamespaceString(tenantID, "foo.bar")) -> "foo.bar" + * + * If multitenancySupport is enabled and featureFlagRequireTenantID is disabled, then tenantId + * is included in the serialization. + * eg. serialize(NamespaceString(tenantID, "foo.bar")) -> "tenantID_foo.bar" + * + * If multitenancySupport is disabled, the tenantID is not set in the NamespaceString Object. + * eg. serialize(NamespaceString(boost::none, "foo.bar")) -> "foo.bar" + */ + static std::string serialize(const NamespaceString& ns); + + /** + * Deserializes StringData ns to a NamespaceString object. + * + * If multitenancySupport is enabled and featureFlagRequireTenantID is enabled, then a + * NamespaceString object is constructed using the tenantId passed in to the constructor. The + * invariant requires tenantID to be initialized and passed to the constructor. + * eg. deserialize(tenantID, "foo.bar") -> NamespaceString(tenantID, "foo.bar") + * + * If multitenancySupport is enabled and featureFlagRequireTenantID is disabled, then ns is + * required to be prefixed with a tenantID. The tenantID parameter is ignored and + * NamespaceString is constructed using only ns. The invariant requires that if a tenantID + * is a parameter, then the tenatID is equal to the prefixed tenantID. + * eg. deserialize(boost::none, "preTenantID_foo.bar") -> NamespaceString(preTenantId, + * "foo.bar") + * + * If multitenancySupport is disabled then the invariant requires tenantID to not be initialized + * and NamespaceString is constructor without the tenantID. + * eg. deserialize(boost::none, "foo.bar") -> NamespaceString(boost::none, "foo.bar") + */ + static NamespaceString deserialize(boost::optional<TenantId> tenantId, StringData ns); +}; + +} // namespace mongo diff --git a/src/mongo/util/namespace_string_util_test.cpp b/src/mongo/util/namespace_string_util_test.cpp new file mode 100644 index 00000000000..60d9edf9770 --- /dev/null +++ b/src/mongo/util/namespace_string_util_test.cpp @@ -0,0 +1,152 @@ +/** + * Copyright (C) 2022-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/db/multitenancy_gen.h" +#include "mongo/db/namespace_string.h" +#include "mongo/idl/server_parameter_test_util.h" +#include "mongo/unittest/death_test.h" +#include "mongo/unittest/unittest.h" +#include "mongo/util/namespace_string_util.h" + +namespace mongo { + +// TenantID is not included in serialization when multitenancySupport and +// featureFlagRequireTenantID are enabled. +TEST(NamespaceStringUtilTest, SerializeMultitenancySupportOnFeatureFlagRequireTenantIDOn) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + TenantId tenantId(OID::gen()); + NamespaceString nss(tenantId, "foo.bar"); + ASSERT_EQ(NamespaceStringUtil::serialize(nss), "foo.bar"); +} + +// TenantID is included in serialization when multitenancySupport is enabled and +// featureFlagRequireTenantID is disabled. +TEST(NamespaceStringUtilTest, SerializeMultitenancySupportOnFeatureFlagRequireTenantIDOff) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", false); + TenantId tenantId(OID::gen()); + std::string tenantNsStr = str::stream() << tenantId.toString() << "_foo.bar"; + NamespaceString nss(tenantId, "foo.bar"); + ASSERT_EQ(NamespaceStringUtil::serialize(nss), tenantNsStr); +} + +// Serialize correctly when multitenancySupport is disabled. +TEST(NamespaceStringUtilTest, SerializeMultitenancySupportOff) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", false); + NamespaceString nss(boost::none, "foo.bar"); + ASSERT_EQ(NamespaceStringUtil::serialize(nss), "foo.bar"); +} + +// Assert that if multitenancySupport and featureFlagRequireTenantID are on, then tenantId is set. +DEATH_TEST_REGEX(NamespaceStringUtilTest, + DeserializeAssertTenantIdSetMultitenancySupportOnFeatureFlagRequireTenantIDOn, + "Invariant failure.*") { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + NamespaceString nss = NamespaceStringUtil::deserialize(boost::none, "foo.bar"); +} + +// Deserialize NamespaceString using the tenantID as a parameter to the NamespaceString constructor +// when multitenancySupport and featureFlagRequireTenantID are enabled and ns does not have prefixed +// tenantID. +TEST(NamespaceStringUtilTest, + DeserializeNSSWithoutPrefixedTenantIDMultitenancySupportOnFeatureFlagRequireTenantIDOn) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + TenantId tenantId(OID::gen()); + NamespaceString nss = NamespaceStringUtil::deserialize(tenantId, "foo.bar"); + ASSERT_EQ(nss.ns(), "foo.bar"); + ASSERT(nss.tenantId()); + ASSERT_EQ(nss, NamespaceString(tenantId, "foo.bar")); +} + +// Assert that if multitenancySupport is enabled and featureFlagRequireTenantID is disabled, +// then tenantId parsed from ns and tenantID passed to NamespaceString object are equal. +DEATH_TEST_REGEX(NamespaceStringUtilTest, + DeserializeAssertTenantIdSetMultitenancySupportOnFeatureFlagRequireTenantIDOff, + "Invariant failure.*") { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", false); + TenantId tenantId(OID::gen()); + TenantId tenantId2(OID::gen()); + std::string tenantNsStr = str::stream() << tenantId.toString() << "_foo.bar"; + NamespaceString nss = NamespaceStringUtil::deserialize(tenantId2, tenantNsStr); +} + +// Deserialize NamespaceString when multitenancySupport is enabled and featureFlagRequireTenantID is +// disabled. +TEST(NamespaceStringUtilTest, DeserializeMultitenancySupportOnFeatureFlagRequireTenantIDOff) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", false); + TenantId tenantId(OID::gen()); + std::string tenantNsStr = str::stream() << tenantId.toString() << "_foo.bar"; + NamespaceString nss = NamespaceStringUtil::deserialize(boost::none, tenantNsStr); + NamespaceString nss1 = NamespaceStringUtil::deserialize(tenantId, tenantNsStr); + ASSERT_EQ(nss.ns(), "foo.bar"); + ASSERT(nss.tenantId()); + ASSERT_EQ(nss, NamespaceString(tenantId, "foo.bar")); + ASSERT_EQ(nss, nss1); +} + +// Assert tenantID is not initialized when multitenancySupport is disabled. +DEATH_TEST_REGEX(NamespaceStringUtilTest, + DeserializeMultitenancySupportOff, + "Invariant failure.*") { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", false); + TenantId tenantId(OID::gen()); + NamespaceStringUtil::deserialize(tenantId, "foo.bar"); +} + +// Deserialize NamespaceString with prefixed tenantId when multitenancySupport and +// featureFlagRequireTenantId are disabled. +TEST(NamespaceStringUtilTest, + DeserializeWithTenantIdInStringMultitenancySupportOffFeatureFlagRequireTenantIDOff) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", false); + TenantId tenantId(OID::gen()); + std::string tenantNsStr = str::stream() << tenantId.toString() << "_foo.bar"; + std::string dbNameStr = str::stream() << tenantId.toString() << "_foo"; + NamespaceString nss = NamespaceStringUtil::deserialize(boost::none, tenantNsStr); + ASSERT_EQ(nss.tenantId(), boost::none); + ASSERT_EQ(nss.dbName().db(), dbNameStr); +} + +// Deserialize NamespaceString when multitenancySupport and featureFlagRequireTenantID are disabled. +TEST(NamespaceStringUtilTest, DeserializeMultitenancySupportOffFeatureFlagRequireTenantIDOff) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", false); + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", false); + NamespaceString nss = NamespaceStringUtil::deserialize(boost::none, "foo.bar"); + ASSERT_EQ(nss.ns(), "foo.bar"); + ASSERT(!nss.tenantId()); + ASSERT_EQ(nss, NamespaceString(boost::none, "foo.bar")); +} + +} // namespace mongo |