diff options
author | Gabriel Marks <gabriel.marks@mongodb.com> | 2022-09-13 16:28:37 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-09-13 20:02:43 +0000 |
commit | 293d41288fa7ccfbc03b8448b44214b2cf84db76 (patch) | |
tree | 34d00a7b53ab924a50de778d470ad412a472c5ed | |
parent | fa534d676f5f673a6a7f3ea656f650ff108b7f92 (diff) | |
download | mongo-293d41288fa7ccfbc03b8448b44214b2cf84db76.tar.gz |
SERVER-68017 Constrain cluster-wide with-storage parameters to be tenant ID aware
-rw-r--r-- | buildscripts/idl/idl/binder.py | 5 | ||||
-rw-r--r-- | src/mongo/db/change_collection_expired_documents_remover.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/commands/set_cluster_parameter_invocation_test.cpp | 65 | ||||
-rw-r--r-- | src/mongo/db/server_parameter.h | 3 | ||||
-rw-r--r-- | src/mongo/db/server_parameter_with_storage.h | 11 | ||||
-rw-r--r-- | src/mongo/idl/cluster_server_parameter_initializer_test.cpp | 18 | ||||
-rw-r--r-- | src/mongo/idl/cluster_server_parameter_op_observer_test.cpp | 40 | ||||
-rw-r--r-- | src/mongo/idl/server_parameter_with_storage_test.cpp | 80 |
8 files changed, 167 insertions, 62 deletions
diff --git a/buildscripts/idl/idl/binder.py b/buildscripts/idl/idl/binder.py index e3f75be5683..6ffaf2d5108 100644 --- a/buildscripts/idl/idl/binder.py +++ b/buildscripts/idl/idl/binder.py @@ -1311,7 +1311,10 @@ def _bind_server_parameter_with_storage(ctxt, ast_param, param): ctxt.add_server_parameter_invalid_attr(param, field, 'bound') return None - ast_param.cpp_vartype = param.cpp_vartype + if param.set_at == ['cluster']: + ast_param.cpp_vartype = f'TenantIdMap<{param.cpp_vartype}>' + else: + ast_param.cpp_vartype = param.cpp_vartype ast_param.cpp_varname = param.cpp_varname ast_param.on_update = param.on_update diff --git a/src/mongo/db/change_collection_expired_documents_remover.cpp b/src/mongo/db/change_collection_expired_documents_remover.cpp index 7c6a9b62eae..80a816945be 100644 --- a/src/mongo/db/change_collection_expired_documents_remover.cpp +++ b/src/mongo/db/change_collection_expired_documents_remover.cpp @@ -63,8 +63,11 @@ std::vector<boost::optional<TenantId>> getAllTenants() { } boost::optional<int64_t> getExpireAfterSeconds(boost::optional<TenantId> tid) { - // TODO SERVER-65950 Fetch 'expiredAfterSeconds' per tenant basis. - return {gChangeStreamsClusterParameter.getExpireAfterSeconds()}; + auto* clusterParameters = ServerParameterSet::getClusterParameterSet(); + auto* changeStreamsParam = + clusterParameters->get<ClusterParameterWithStorage<ChangeStreamsClusterParameterStorage>>( + "changeStreams"); + return changeStreamsParam->getValue(tid).getExpireAfterSeconds(); } void removeExpiredDocuments(Client* client) { diff --git a/src/mongo/db/commands/set_cluster_parameter_invocation_test.cpp b/src/mongo/db/commands/set_cluster_parameter_invocation_test.cpp index 3e99e94cf96..0623360a00e 100644 --- a/src/mongo/db/commands/set_cluster_parameter_invocation_test.cpp +++ b/src/mongo/db/commands/set_cluster_parameter_invocation_test.cpp @@ -99,7 +99,8 @@ private: class DBClientMock : public DBClientService { public: - DBClientMock(std::function<StatusWith<bool>(BSONObj, BSONObj)> updateParameterOnDiskMock) { + DBClientMock(std::function<StatusWith<bool>(BSONObj, BSONObj, const boost::optional<TenantId>&)> + updateParameterOnDiskMock) { this->updateParameterOnDiskMockImpl = updateParameterOnDiskMock; } @@ -108,7 +109,7 @@ public: BSONObj info, const WriteConcernOptions&, const boost::optional<TenantId>& tenantId) override { - return updateParameterOnDiskMockImpl(cmd, info); + return updateParameterOnDiskMockImpl(cmd, info, tenantId); } Timestamp getUpdateClusterTime(OperationContext*) override { @@ -117,7 +118,8 @@ public: } private: - std::function<StatusWith<bool>(BSONObj, BSONObj)> updateParameterOnDiskMockImpl; + std::function<StatusWith<bool>(BSONObj, BSONObj, const boost::optional<TenantId>&)> + updateParameterOnDiskMockImpl; }; MockServerParameter alwaysValidatingServerParameter(StringData name) { @@ -137,13 +139,22 @@ MockServerParameter alwaysInvalidatingServerParameter(StringData name) { } DBClientMock alwaysSucceedingDbClient() { - DBClientMock dbServiceMock([&](BSONObj cmd, BSONObj info) { return true; }); + DBClientMock dbServiceMock( + [&](BSONObj, BSONObj, const boost::optional<TenantId>&) { return true; }); + + return dbServiceMock; +} + +DBClientMock tenantIdReportingDbClient() { + DBClientMock dbServiceMock([&](BSONObj, BSONObj, const boost::optional<TenantId>& tenantId) { + return Status(ErrorCodes::UnknownError, tenantId ? tenantId->toString() : ""); + }); return dbServiceMock; } DBClientMock alwaysFailingDbClient() { - DBClientMock dbServiceMock([&](BSONObj cmd, BSONObj info) { + DBClientMock dbServiceMock([&](BSONObj, BSONObj, const boost::optional<TenantId>&) { return Status(ErrorCodes::UnknownError, "DB Client Update Failed"); }); @@ -300,5 +311,49 @@ TEST(SetClusterParameterCommand, ThrowsWhenParameterNotPresent) { DBException, ErrorCodes::NoSuchKey); } + +TEST(SetClusterParameterCommand, TenantIdPassesThrough) { + + DBClientMock dbServiceMock = tenantIdReportingDbClient(); + MockServerParameter sp = alwaysValidatingServerParameter("TenantIdPassesThroughParameter"_sd); + + auto serviceCtx = ServiceContext::make(); + auto client = serviceCtx->makeClient("SomeTest"); + + auto mpsPtr = std::make_unique<MockParameterService>([&](StringData s) { return &sp; }); + + Client* clientPtr = client.get(); + + BSONObjBuilder testCmdBson; + testCmdBson << "testCommand" + << BSON("ok" + << "someval"); + + BSONObj obj = testCmdBson.obj(); + + SetClusterParameterInvocation fixture(std::move(mpsPtr), dbServiceMock); + + OperationContext spyCtx(clientPtr, 1234); + + TenantId tenantId(OID("123456789012345678901234")); + + SetClusterParameter testCmdNoTenant(obj); + + ASSERT_THROWS_CODE_AND_WHAT( + fixture.invoke(&spyCtx, testCmdNoTenant, boost::none, kMajorityWriteConcern), + DBException, + ErrorCodes::UnknownError, + ""); + + SetClusterParameter testCmdWithTenant(obj); + testCmdWithTenant.setDbName(NamespaceString::makeClusterParametersNSS(tenantId).dbName()); + + ASSERT_THROWS_CODE_AND_WHAT( + fixture.invoke(&spyCtx, testCmdWithTenant, boost::none, kMajorityWriteConcern), + DBException, + ErrorCodes::UnknownError, + tenantId.toString()); +} + } // namespace } // namespace mongo diff --git a/src/mongo/db/server_parameter.h b/src/mongo/db/server_parameter.h index 0f69433e9f3..31ffd29ee2a 100644 --- a/src/mongo/db/server_parameter.h +++ b/src/mongo/db/server_parameter.h @@ -91,6 +91,9 @@ enum class ServerParameterType { class ServerParameterSet; class OperationContext; +template <typename U> +using TenantIdMap = std::map<boost::optional<TenantId>, U>; + class ServerParameter { public: using Map = std::map<std::string, ServerParameter*>; diff --git a/src/mongo/db/server_parameter_with_storage.h b/src/mongo/db/server_parameter_with_storage.h index 6e39fe27576..423509ae476 100644 --- a/src/mongo/db/server_parameter_with_storage.h +++ b/src/mongo/db/server_parameter_with_storage.h @@ -63,9 +63,6 @@ constexpr bool hasClusterServerParameter = stdx::is_detected_v<HasClusterServerP namespace idl_server_parameter_detail { -template <typename U> -using TenantIdMap = std::map<boost::optional<TenantId>, U>; - // Predicate rules for bounds conditions struct GT { @@ -308,8 +305,8 @@ private: public: using element_type = typename SW::type; - // TODO SERVER-68017 Tenant aware parameters are currently unsupported. - static_assert(!SW::isTenantAware); + // Cluster parameters must be tenant-aware. + static_assert(SW::isTenantAware || paramType != SPT::kClusterWide); // Compile-time assertion to ensure that IDL-defined in-memory storage for CSPs are // chained to the ClusterServerParameter base type. @@ -529,6 +526,10 @@ private: std::once_flag _setDefaultOnce; }; +template <typename Storage> +using ClusterParameterWithStorage = + IDLServerParameterWithStorage<ServerParameterType::kClusterWide, TenantIdMap<Storage>>; + // MSVC has trouble resolving T=decltype(param) through the above class template. // Avoid that by using this proxy factory to infer storage type. template <ServerParameterType paramType, typename T> diff --git a/src/mongo/idl/cluster_server_parameter_initializer_test.cpp b/src/mongo/idl/cluster_server_parameter_initializer_test.cpp index fd9e1a8234d..f104f7e21d7 100644 --- a/src/mongo/idl/cluster_server_parameter_initializer_test.cpp +++ b/src/mongo/idl/cluster_server_parameter_initializer_test.cpp @@ -44,6 +44,8 @@ namespace mongo { namespace { using namespace cluster_server_parameter_test_util; +typedef ClusterParameterWithStorage<ClusterServerParameterTest> ClusterTestParameter; + class ClusterServerParameterInitializerTest : public ClusterServerParameterTestBase { public: void setUp() final { @@ -89,9 +91,7 @@ protected: TEST_F(ClusterServerParameterInitializerTest, OnInitialSync) { // Retrieve the in-memory test cluster server parameter and ensure it's set to the default // value. - auto* sp = ServerParameterSet::getClusterParameterSet() - ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, - ClusterServerParameterTest>>(kCSPTest); + auto* sp = ServerParameterSet::getClusterParameterSet()->get<ClusterTestParameter>(kCSPTest); ASSERT(sp != nullptr); ClusterServerParameterTest cspTest = sp->getValue(boost::none); ASSERT_EQ(cspTest.getIntValue(), kDefaultIntValue); @@ -100,9 +100,7 @@ TEST_F(ClusterServerParameterInitializerTest, OnInitialSync) { // Indicate that data is available at the end of initial sync and check that the in-memory data // is updated. doInitialSync(); - sp = ServerParameterSet::getClusterParameterSet() - ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, - ClusterServerParameterTest>>(kCSPTest); + sp = ServerParameterSet::getClusterParameterSet()->get<ClusterTestParameter>(kCSPTest); ASSERT(sp != nullptr); cspTest = sp->getValue(boost::none); ASSERT_EQ(cspTest.getIntValue(), kInitialIntValue); @@ -111,9 +109,7 @@ TEST_F(ClusterServerParameterInitializerTest, OnInitialSync) { TEST_F(ClusterServerParameterInitializerTest, OnStartupRecovery) { // Retrieve the test cluster server parameter and ensure it's set to the default value. - auto* sp = ServerParameterSet::getClusterParameterSet() - ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, - ClusterServerParameterTest>>(kCSPTest); + auto* sp = ServerParameterSet::getClusterParameterSet()->get<ClusterTestParameter>(kCSPTest); ASSERT(sp != nullptr); ClusterServerParameterTest cspTest = sp->getValue(boost::none); ASSERT_EQ(cspTest.getIntValue(), kDefaultIntValue); @@ -122,9 +118,7 @@ TEST_F(ClusterServerParameterInitializerTest, OnStartupRecovery) { // Indicate that data is available at the end of startup recovery and check that the in-memory // data is updated. doStartupRecovery(); - sp = ServerParameterSet::getClusterParameterSet() - ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, - ClusterServerParameterTest>>(kCSPTest); + sp = ServerParameterSet::getClusterParameterSet()->get<ClusterTestParameter>(kCSPTest); ASSERT(sp != nullptr); cspTest = sp->getValue(boost::none); ASSERT_EQ(cspTest.getIntValue(), kInitialIntValue); diff --git a/src/mongo/idl/cluster_server_parameter_op_observer_test.cpp b/src/mongo/idl/cluster_server_parameter_op_observer_test.cpp index 3b149fdae2c..87efd29a2a9 100644 --- a/src/mongo/idl/cluster_server_parameter_op_observer_test.cpp +++ b/src/mongo/idl/cluster_server_parameter_op_observer_test.cpp @@ -45,6 +45,8 @@ const std::vector<NamespaceString> kIgnoredNamespaces = { NamespaceString("local"_sd, "clusterParameters"_sd), NamespaceString("test"_sd, "foo"_sd)}; +typedef ClusterParameterWithStorage<ClusterServerParameterTest> ClusterTestParameter; + class ClusterServerParameterOpObserverTest : public ClusterServerParameterTestBase { public: void setUp() override { @@ -128,9 +130,8 @@ public: // Asserts that the parameter state does not change for this action. template <typename F> void assertIgnored(const NamespaceString& nss, F fn) { - auto* sp = ServerParameterSet::getClusterParameterSet() - ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, - ClusterServerParameterTest>>(kCSPTest); + auto* sp = + ServerParameterSet::getClusterParameterSet()->get<ClusterTestParameter>(kCSPTest); ASSERT(sp != nullptr); const auto initialCPTime = sp->getClusterParameterTime(boost::none); @@ -151,9 +152,8 @@ public: upsert(doc); doInserts(NamespaceString::kClusterParametersNamespace, {doc}); - auto* sp = ServerParameterSet::getClusterParameterSet() - ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, - ClusterServerParameterTest>>(kCSPTest); + auto* sp = + ServerParameterSet::getClusterParameterSet()->get<ClusterTestParameter>(kCSPTest); ASSERT(sp != nullptr); ClusterServerParameterTest cspTest = sp->getValue(boost::none); @@ -183,9 +183,7 @@ protected: }; TEST_F(ClusterServerParameterOpObserverTest, OnInsertRecord) { - auto* sp = ServerParameterSet::getClusterParameterSet() - ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, - ClusterServerParameterTest>>(kCSPTest); + auto* sp = ServerParameterSet::getClusterParameterSet()->get<ClusterTestParameter>(kCSPTest); ASSERT(sp != nullptr); // Single record insert. @@ -251,9 +249,7 @@ TEST_F(ClusterServerParameterOpObserverTest, OnInsertRecord) { TEST_F(ClusterServerParameterOpObserverTest, OnUpdateRecord) { initializeState(); - auto* sp = ServerParameterSet::getClusterParameterSet() - ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, - ClusterServerParameterTest>>(kCSPTest); + auto* sp = ServerParameterSet::getClusterParameterSet()->get<ClusterTestParameter>(kCSPTest); ASSERT(sp != nullptr); // Single record update. @@ -283,9 +279,7 @@ TEST_F(ClusterServerParameterOpObserverTest, OnUpdateRecord) { } TEST_F(ClusterServerParameterOpObserverTest, onDeleteRecord) { - auto* sp = ServerParameterSet::getClusterParameterSet() - ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, - ClusterServerParameterTest>>(kCSPTest); + auto* sp = ServerParameterSet::getClusterParameterSet()->get<ClusterTestParameter>(kCSPTest); ASSERT(sp != nullptr); const auto initialDoc = initializeState(); @@ -328,9 +322,7 @@ TEST_F(ClusterServerParameterOpObserverTest, onDropDatabase) { // Actually drop the config DB. doDropDatabase(kConfigDB); - auto* sp = ServerParameterSet::getClusterParameterSet() - ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, - ClusterServerParameterTest>>(kCSPTest); + auto* sp = ServerParameterSet::getClusterParameterSet()->get<ClusterTestParameter>(kCSPTest); ASSERT(sp != nullptr); ClusterServerParameterTest cspTest = sp->getValue(boost::none); @@ -346,9 +338,7 @@ TEST_F(ClusterServerParameterOpObserverTest, onRenameCollection) { assertIgnoredOtherNamespaces([&](const auto& nss) { doRenameCollection(nss, kTestFoo); }); assertIgnoredOtherNamespaces([&](const auto& nss) { doRenameCollection(kTestFoo, nss); }); - auto* sp = ServerParameterSet::getClusterParameterSet() - ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, - ClusterServerParameterTest>>(kCSPTest); + auto* sp = ServerParameterSet::getClusterParameterSet()->get<ClusterTestParameter>(kCSPTest); ASSERT(sp != nullptr); // These renames "work" despite not mutating durable state @@ -374,9 +364,7 @@ TEST_F(ClusterServerParameterOpObserverTest, onImportCollection) { // Import ignorable collections. assertIgnoredOtherNamespaces([&](const auto& nss) { doImportCollection(nss); }); - auto* sp = ServerParameterSet::getClusterParameterSet() - ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, - ClusterServerParameterTest>>(kCSPTest); + auto* sp = ServerParameterSet::getClusterParameterSet()->get<ClusterTestParameter>(kCSPTest); ASSERT(sp != nullptr); // Import the collection (rescan). @@ -396,9 +384,7 @@ TEST_F(ClusterServerParameterOpObserverTest, onReplicationRollback) { // Import ignorable collections. assertIgnoredOtherNamespaces([&](const auto& nss) { doImportCollection(nss); }); - auto* sp = ServerParameterSet::getClusterParameterSet() - ->get<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, - ClusterServerParameterTest>>(kCSPTest); + auto* sp = ServerParameterSet::getClusterParameterSet()->get<ClusterTestParameter>(kCSPTest); ASSERT(sp != nullptr); // Trigger rollback of ignorable namespaces. diff --git a/src/mongo/idl/server_parameter_with_storage_test.cpp b/src/mongo/idl/server_parameter_with_storage_test.cpp index b9a22ed689b..3944c9e0ce0 100644 --- a/src/mongo/idl/server_parameter_with_storage_test.cpp +++ b/src/mongo/idl/server_parameter_with_storage_test.cpp @@ -327,8 +327,7 @@ TEST(IDLServerParameterWithStorage, RAIIServerParameterController) { TEST(IDLServerParameterWithStorage, CSPStorageTest) { // Retrieve the cluster IDLServerParameterWithStorage. auto* clusterParam = - dynamic_cast<IDLServerParameterWithStorage<ServerParameterType::kClusterWide, - test::ChangeStreamOptionsClusterParam>*>( + dynamic_cast<ClusterParameterWithStorage<test::ChangeStreamOptionsClusterParam>*>( getClusterServerParameter("changeStreamOptions")); // Check that current value is the default value. @@ -360,14 +359,16 @@ TEST(IDLServerParameterWithStorage, CSPStorageTest) { ASSERT_EQ(test::count, 1); // Append to BSONObj and verify that expected fields are present. - BSONObjBuilder b; - clusterParam->append(nullptr, &b, clusterParam->name(), boost::none); - auto obj = b.obj(); - ASSERT_EQ(obj.nFields(), 4); - ASSERT_EQ(obj["_id"_sd].String(), "changeStreamOptions"); - ASSERT_EQ(obj["preAndPostImages"_sd].Obj()["expireAfterSeconds"].Long(), 40); - ASSERT_EQ(obj["testStringField"_sd].String(), "testString"); - ASSERT_EQ(obj["clusterParameterTime"_sd].timestamp(), updateTime.asTimestamp()); + { + BSONObjBuilder b; + clusterParam->append(nullptr, &b, clusterParam->name(), boost::none); + auto obj = b.obj(); + ASSERT_EQ(obj.nFields(), 4); + ASSERT_EQ(obj["_id"_sd].String(), "changeStreamOptions"); + ASSERT_EQ(obj["preAndPostImages"_sd].Obj()["expireAfterSeconds"].Long(), 40); + ASSERT_EQ(obj["testStringField"_sd].String(), "testString"); + ASSERT_EQ(obj["clusterParameterTime"_sd].timestamp(), updateTime.asTimestamp()); + } // setFromString should fail for cluster server parameters. ASSERT_NOT_OK(clusterParam->setFromString("", boost::none)); @@ -417,6 +418,65 @@ TEST(IDLServerParameterWithStorage, CSPStorageTest) { ASSERT_EQ(retrievedParam.getTestStringField(), "default"); ASSERT_EQ(clusterParam->getClusterParameterTime(boost::none), LogicalTime::kUninitialized); ASSERT_EQ(test::count, 3); + + // Different tenants should access separate sets of cluster parameters. + auto tenant1 = TenantId(OID("1234567890abcdef12345678")); + auto tenant2 = TenantId(OID("1234567890abcdef12345679")); + + // New tenants should start w/ the defaults. + auto retrievedParam1 = clusterParam->getValue(tenant1); + auto retrievedParam2 = clusterParam->getValue(tenant2); + ASSERT_EQ(retrievedParam1.getPreAndPostImages().getExpireAfterSeconds(), 35); + ASSERT_EQ(retrievedParam2.getPreAndPostImages().getExpireAfterSeconds(), 35); + + // Setting for one tenant should not change any other tenants. + ASSERT_OK(clusterParam->ServerParameter::set(newDefaultParam.toBSON(), tenant1)); + retrievedParam = clusterParam->getValue(boost::none); + retrievedParam1 = clusterParam->getValue(tenant1); + retrievedParam2 = clusterParam->getValue(tenant2); + ASSERT_EQ(retrievedParam.getPreAndPostImages().getExpireAfterSeconds(), 35); + ASSERT_EQ(retrievedParam1.getPreAndPostImages().getExpireAfterSeconds(), 45); + ASSERT_EQ(retrievedParam2.getPreAndPostImages().getExpireAfterSeconds(), 35); + + updatedPrePostImgs.setExpireAfterSeconds(40); + updatedParam.setPreAndPostImages(updatedPrePostImgs); + ASSERT_OK(clusterParam->ServerParameter::set(updatedParam.toBSON(), boost::none)); + retrievedParam = clusterParam->getValue(boost::none); + retrievedParam1 = clusterParam->getValue(tenant1); + retrievedParam2 = clusterParam->getValue(tenant2); + ASSERT_EQ(retrievedParam.getPreAndPostImages().getExpireAfterSeconds(), 40); + ASSERT_EQ(retrievedParam1.getPreAndPostImages().getExpireAfterSeconds(), 45); + ASSERT_EQ(retrievedParam2.getPreAndPostImages().getExpireAfterSeconds(), 35); + + // Resetting one tenant should not change any other tenants. + ASSERT_OK(clusterParam->reset(tenant1)); + retrievedParam = clusterParam->getValue(boost::none); + retrievedParam1 = clusterParam->getValue(tenant1); + retrievedParam2 = clusterParam->getValue(tenant2); + ASSERT_EQ(retrievedParam.getPreAndPostImages().getExpireAfterSeconds(), 40); + ASSERT_EQ(retrievedParam1.getPreAndPostImages().getExpireAfterSeconds(), 35); + ASSERT_EQ(retrievedParam2.getPreAndPostImages().getExpireAfterSeconds(), 35); + + // Append should append only the tenant specified. + ASSERT_OK(clusterParam->ServerParameter::set(newDefaultParam.toBSON(), tenant2)); + { + BSONObjBuilder b; + clusterParam->append(nullptr, &b, clusterParam->name(), boost::none); + auto obj = b.obj(); + ASSERT_EQ(obj["preAndPostImages"_sd].Obj()["expireAfterSeconds"].Long(), 40); + } + { + BSONObjBuilder b; + clusterParam->append(nullptr, &b, clusterParam->name(), tenant1); + auto obj = b.obj(); + ASSERT_EQ(obj["preAndPostImages"_sd].Obj()["expireAfterSeconds"].Long(), 35); + } + { + BSONObjBuilder b; + clusterParam->append(nullptr, &b, clusterParam->name(), tenant2); + auto obj = b.obj(); + ASSERT_EQ(obj["preAndPostImages"_sd].Obj()["expireAfterSeconds"].Long(), 45); + } } } // namespace |