diff options
author | jannaerin <golden.janna@gmail.com> | 2021-12-20 17:03:13 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-12-20 18:26:25 +0000 |
commit | b53fc88485f274d9b20cc93b53058f77d8859d93 (patch) | |
tree | 3768f4a4a5a382df1b594cf690fa4214285f57e4 | |
parent | 3b11d6fa60ff69f9ae52a90690cd05404625284e (diff) | |
download | mongo-b53fc88485f274d9b20cc93b53058f77d8859d93.tar.gz |
SERVER-61983 Create TenantNamespace class
-rw-r--r-- | src/mongo/db/SConscript | 3 | ||||
-rw-r--r-- | src/mongo/db/tenant_namespace.h | 161 | ||||
-rw-r--r-- | src/mongo/db/tenant_namespace_test.cpp | 138 |
3 files changed, 302 insertions, 0 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 5bf18e77cb8..2becfdb5dfc 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -2485,6 +2485,7 @@ if wiredtiger: 'session_catalog_mongod_test.cpp', 'session_catalog_test.cpp', 'startup_warnings_mongod_test.cpp', + 'tenant_namespace_test.cpp', 'thread_client_test.cpp', 'time_proof_service_test.cpp', 'transaction_api_test.cpp', @@ -2541,6 +2542,7 @@ if wiredtiger: 'logical_session_id_helpers', 'logical_time', 'mirror_maestro', + 'multitenancy_params', 'namespace_string', 'op_observer', 'op_observer_impl', @@ -2558,6 +2560,7 @@ if wiredtiger: 'repl/storage_interface_impl', 'rw_concern_d', 's/shard_server_test_fixture', + 'server_feature_flags', 'server_options_core', 'server_options_servers', 'service_context', diff --git a/src/mongo/db/tenant_namespace.h b/src/mongo/db/tenant_namespace.h new file mode 100644 index 00000000000..080a12c5ead --- /dev/null +++ b/src/mongo/db/tenant_namespace.h @@ -0,0 +1,161 @@ +/** + * Copyright (C) 2021-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 <algorithm> +#include <boost/optional.hpp> +#include <iosfwd> +#include <string> + +#include "mongo/base/string_data.h" +#include "mongo/bson/util/builder.h" +#include "mongo/db/multitenancy_gen.h" +#include "mongo/db/namespace_string.h" +#include "mongo/db/server_feature_flags_gen.h" +#include "mongo/logv2/log_attr.h" + +namespace mongo { + +class TenantNamespace { +public: + /** + * Constructs an empty TenantNamespace. + */ + TenantNamespace() : _tenantId(boost::none), _nss(), _tenantNsStr() {} + + /** + * Constructs a TenantNamespace from the given tenantId and NamespaceString. + * + * If featureFlagRequireTenantID is set, tenantId is required. + */ + TenantNamespace(boost::optional<mongo::OID> tenantId, NamespaceString nss) { + // TODO SERVER-62114 Check instead if gMultitenancySupport is enabled. + if (gFeatureFlagRequireTenantID.isEnabledAndIgnoreFCV()) + invariant(tenantId); + + _tenantId = tenantId; + _nss = nss; + _tenantNsStr = _tenantId + ? boost::make_optional(_tenantId->toString() + "_" + _nss.toString()) + : boost::none; + } + + /** + * Constructs a TenantNamespace from the string "ns". When the server parameter + * "multitenancySupport” is enabled, the tenantId will be parsed separately from the database + * name. If it is disabled, the tenantId will be parsed as a prefix of the database name, and + * the tenantId field will be empty. For example: + * if “multitenancySupport” is enabled, "tenant1_dbA.collA" will be parsed as: + * _tenantId = tenant1 + * _nss = NamespaceString(dbA.collA) + * + * if “multitenancySupport” is disabled, "tenant1_dbA.collA" will be parsed as: + * _tenantId = boost::none + * _nss = NamespaceString(tenant1_dbA.collA), and the _nss,db() + * + * This method should only be used when reading a namespace from disk. To construct a + * TenantNamespace otherwise, use the standard constructor above. + * + * If featureFlagRequireTenantID is set, tenantId is required. + */ + static TenantNamespace parseTenantNamespaceFromDisk(StringData ns) { + if (!gMultitenancySupport) { + return TenantNamespace(boost::none, NamespaceString(ns)); + } + + auto tenantDelim = ns.find('_'); + if (tenantDelim == std::string::npos) + return TenantNamespace(boost::none, NamespaceString(ns)); + + auto tenantId = OID(ns.substr(0, tenantDelim)); + auto nss = NamespaceString(ns.substr(tenantDelim + 1, ns.size() - 1 - tenantDelim)); + return TenantNamespace(tenantId, nss); + } + + boost::optional<mongo::OID> tenantId() const { + return _tenantId; + } + + StringData db() const { + return _nss.db(); + } + + StringData coll() const { + return _nss.coll(); + } + + NamespaceString getNss() const { + return _nss; + } + + std::string toString() const { + if (_tenantNsStr) + return *_tenantNsStr; + + invariant(!_tenantId); + return _nss.ns(); + } + + // Relops among `TenantNamespace`. + friend bool operator==(const TenantNamespace& a, const TenantNamespace& b) { + return a.toString() == b.toString(); + } + friend bool operator!=(const TenantNamespace& a, const TenantNamespace& b) { + return a.toString() != b.toString(); + } + friend bool operator<(const TenantNamespace& a, const TenantNamespace& b) { + return a.toString() < b.toString(); + } + friend bool operator>(const TenantNamespace& a, const TenantNamespace& b) { + return a.toString() > b.toString(); + } + friend bool operator<=(const TenantNamespace& a, const TenantNamespace& b) { + return a.toString() <= b.toString(); + } + friend bool operator>=(const TenantNamespace& a, const TenantNamespace& b) { + return a.toString() >= b.toString(); + } + + template <typename H> + friend H AbslHashValue(H h, const TenantNamespace& tenantNs) { + return H::combine(std::move(h), tenantNs.toString()); + } + + friend auto logAttrs(const TenantNamespace& nss) { + return "tenantNamespace"_attr = nss; + } + +private: + boost::optional<mongo::OID> _tenantId; + NamespaceString _nss; + boost::optional<std::string> _tenantNsStr; // Only set if _tenantId exists +}; + +} // namespace mongo diff --git a/src/mongo/db/tenant_namespace_test.cpp b/src/mongo/db/tenant_namespace_test.cpp new file mode 100644 index 00000000000..d3f3f418ba5 --- /dev/null +++ b/src/mongo/db/tenant_namespace_test.cpp @@ -0,0 +1,138 @@ +/** + * Copyright (C) 2021-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/server_feature_flags_gen.h" +#include "mongo/db/tenant_namespace.h" +#include "mongo/idl/server_parameter_test_util.h" +#include "mongo/unittest/death_test.h" +#include "mongo/unittest/unittest.h" + +namespace mongo { +namespace { + +TEST(TenantNamespaceTest, TenantNamespaceMultitenancySupportDisabledBasic) { + TenantNamespace tenantNs(boost::none, NamespaceString("a.b")); + ASSERT(!tenantNs.tenantId()); + ASSERT_EQUALS(std::string("a"), tenantNs.db()); + ASSERT_EQUALS(std::string("b"), tenantNs.coll()); + ASSERT_EQUALS(std::string("a.b"), tenantNs.toString()); +} + +TEST(TenantNamespaceTest, TenantNamespaceParseFromDiskMultitenancySupportDisabled) { + TenantNamespace tenantNs = TenantNamespace::parseTenantNamespaceFromDisk("a.b"); + ASSERT(!tenantNs.tenantId()); + ASSERT_EQUALS(std::string("a"), tenantNs.db()); + ASSERT_EQUALS(std::string("b"), tenantNs.coll()); + + mongo::OID tenantId = OID::gen(); + std::string ns = tenantId.toString() + "_a.b"; + TenantNamespace tenantNs2 = TenantNamespace::parseTenantNamespaceFromDisk(ns); + ASSERT(!tenantNs2.tenantId()); + ASSERT_EQUALS(std::string(tenantId.toString() + "_a"), tenantNs2.db()); + ASSERT_EQUALS(std::string("b"), tenantNs2.coll()); +} + +TEST(TenantNamespaceTest, TenantNamespaceMultitenancySupportEnabledFeatureFlagDisabledBasic) { + // TODO SERVER-62114 Remove this test case. + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + + // If the feature flag is disabled, it's acceptable for the tenantId not to exist. + TenantNamespace tenantNs(boost::none, NamespaceString("a.b")); + ASSERT(!tenantNs.tenantId()); + ASSERT_EQUALS(std::string("a"), tenantNs.db()); + ASSERT_EQUALS(std::string("b"), tenantNs.coll()); + ASSERT_EQUALS(std::string("a.b"), tenantNs.toString()); + + // If the feature flag is disabled but a tenantId is given, the tenantId should be parsed + // separately from the db name. + mongo::OID tenantId = OID::gen(); + TenantNamespace tenantNs2(tenantId, NamespaceString("a.b")); + ASSERT(tenantNs2.tenantId()); + ASSERT_EQUALS(tenantId, *tenantNs2.tenantId()); + ASSERT_EQUALS(std::string("a"), tenantNs2.db()); + ASSERT_EQUALS(std::string("b"), tenantNs2.coll()); + ASSERT_EQUALS(std::string(tenantId.toString() + "_a.b"), tenantNs2.toString()); +} + +DEATH_TEST(TenantNamespaceTest, + TenantNamespaceMultitenancySupportEnabledTenantIDRequired, + "invariant") { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + // TODO SERVER-62114 Remove enabling this feature flag. + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + + // A tenantId is not included, so the server should crash + TenantNamespace(boost::none, NamespaceString("a.b")); +} + +DEATH_TEST(TenantNamespaceTest, + TenantNamespaceParseFromDiskMultitenancySupportEnabledTenantIDRequired, + "invariant") { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + // TODO SERVER-62114 Remove enabling this feature flag. + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + + // A tenantId is not included, so the server should crash + auto tenantNs = TenantNamespace::parseTenantNamespaceFromDisk("a.b"); +} + +TEST(TenantNamespaceTest, TenantNamespaceMultitenancySupportEnabledBasic) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + // TODO SERVER-62114 Remove enabling this feature flag. + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + + mongo::OID tenantId = OID::gen(); + TenantNamespace tenantNs(tenantId, NamespaceString("a.b")); + ASSERT(tenantNs.tenantId()); + ASSERT_EQUALS(tenantId, *tenantNs.tenantId()); + ASSERT_EQUALS(std::string("a"), tenantNs.db()); + ASSERT_EQUALS(std::string("b"), tenantNs.coll()); + ASSERT_EQUALS(std::string(tenantId.toString() + "_a.b"), tenantNs.toString()); +} + +TEST(TenantNamespaceTest, TenantNamespaceParseFromDiskMultitenancySupportEnabled) { + RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true); + // TODO SERVER-62114 Remove enabling this feature flag. + RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true); + + mongo::OID tenantId = OID::gen(); + std::string tenantNsStr = str::stream() << tenantId << "_a.b"; + + TenantNamespace tenantNs = TenantNamespace::parseTenantNamespaceFromDisk(tenantNsStr); + ASSERT(tenantNs.tenantId()); + ASSERT_EQUALS(tenantId, *tenantNs.tenantId()); + ASSERT_EQUALS(std::string("a"), tenantNs.db()); + ASSERT_EQUALS(std::string("b"), tenantNs.coll()); +} + +} // namespace +} // namespace mongo |