summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAdam Rayner <adam.rayner@gmail.com>2022-03-08 15:35:04 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-03-30 07:19:10 +0000
commitdca101cdb1370e25ebf1730a46467a820919c1d5 (patch)
tree5b2458a6e1f1f86eefe9c8d2ab18fa4f42c21243 /src
parentb7cec3249e810734a34f2a095d1c994f293d201e (diff)
downloadmongo-dca101cdb1370e25ebf1730a46467a820919c1d5.tar.gz
SERVER-64326 setClusterParameter command unpacks object parameters and valdiates
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/commands/SConscript18
-rw-r--r--src/mongo/db/commands/set_cluster_parameter_command.cpp24
-rw-r--r--src/mongo/db/commands/set_cluster_parameter_invocation.cpp128
-rw-r--r--src/mongo/db/commands/set_cluster_parameter_invocation.h79
-rw-r--r--src/mongo/db/commands/set_cluster_parameter_invocation_test.cpp281
5 files changed, 523 insertions, 7 deletions
diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript
index 29e58008a5b..37055dd8a94 100644
--- a/src/mongo/db/commands/SConscript
+++ b/src/mongo/db/commands/SConscript
@@ -623,7 +623,7 @@ env.Library(
'rwc_defaults_commands',
'server_status',
'servers',
- 'set_cluster_parameter_idl',
+ 'set_cluster_parameter_invocation',
'set_feature_compatibility_version_idl',
'set_index_commit_quorum_idl',
'set_user_write_block_mode_idl',
@@ -637,6 +637,18 @@ env.Library(
)
env.Library(
+ target='set_cluster_parameter_invocation',
+ source=[
+ 'set_cluster_parameter_invocation.cpp',
+ ],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/base',
+ '$BUILD_DIR/mongo/s/write_ops/cluster_write_ops',
+ 'set_cluster_parameter_idl'
+ ],
+)
+
+env.Library(
target='kill_common',
source=[
'kill_op_cmd_base.cpp',
@@ -814,6 +826,7 @@ env.CppUnitTest(
"mr_test.cpp" if get_option('js-engine') != 'none' else [],
"parse_log_component_settings_test.cpp",
"plan_cache_commands_test.cpp",
+ "set_cluster_parameter_invocation_test.cpp",
],
LIBDEPS=[
"$BUILD_DIR/mongo/db/auth/authmocks",
@@ -834,6 +847,7 @@ env.CppUnitTest(
"create_command",
"mongod",
"servers",
+ "set_cluster_parameter_invocation",
"standalone",
],
-)
+) \ No newline at end of file
diff --git a/src/mongo/db/commands/set_cluster_parameter_command.cpp b/src/mongo/db/commands/set_cluster_parameter_command.cpp
index 0abcb976955..b5285dbeae3 100644
--- a/src/mongo/db/commands/set_cluster_parameter_command.cpp
+++ b/src/mongo/db/commands/set_cluster_parameter_command.cpp
@@ -27,14 +27,13 @@
* it in the license file.
*/
-#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kCommand
-
#include "mongo/platform/basic.h"
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/commands.h"
#include "mongo/db/commands/set_cluster_parameter_gen.h"
-#include "mongo/logv2/log.h"
+#include "mongo/db/commands/set_cluster_parameter_invocation.h"
+#include "mongo/idl/cluster_server_parameter_gen.h"
namespace mongo {
@@ -60,17 +59,32 @@ public:
public:
using InvocationBase::InvocationBase;
- void typedRun(OperationContext*) {
- LOGV2(6226501, "Received setClusterParameter on node");
+ void typedRun(OperationContext* opCtx) {
+
+ uassert(ErrorCodes::IllegalOperation,
+ "Cannot set cluster parameter, gFeatureFlagClusterWideConfig is not enabled",
+ gFeatureFlagClusterWideConfig.isEnabledAndIgnoreFCV());
+
+ std::unique_ptr<ServerParameterService> parameterService =
+ std::make_unique<ClusterParameterService>();
+
+ DBDirectClient dbClient(opCtx);
+ ClusterParameterDBClientService dbService(dbClient);
+
+ SetClusterParameterInvocation invocation{std::move(parameterService), dbService};
+
+ invocation.invoke(opCtx, request());
}
private:
bool supportsWriteConcern() const override {
return false;
}
+
NamespaceString ns() const override {
return NamespaceString();
}
+
void doCheckAuthorization(OperationContext* opCtx) const override {
uassert(ErrorCodes::Unauthorized,
"Unauthorized",
diff --git a/src/mongo/db/commands/set_cluster_parameter_invocation.cpp b/src/mongo/db/commands/set_cluster_parameter_invocation.cpp
new file mode 100644
index 00000000000..4cb1087996a
--- /dev/null
+++ b/src/mongo/db/commands/set_cluster_parameter_invocation.cpp
@@ -0,0 +1,128 @@
+/**
+ * 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.
+ */
+
+#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kCommand
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/auth/authorization_session.h"
+#include "mongo/db/commands.h"
+#include "mongo/db/commands/set_cluster_parameter_gen.h"
+#include "mongo/db/commands/set_cluster_parameter_invocation.h"
+#include "mongo/db/vector_clock.h"
+#include "mongo/idl/cluster_server_parameter_gen.h"
+#include "mongo/logv2/log.h"
+#include "mongo/s/write_ops/batched_command_response.h"
+
+namespace mongo {
+
+void SetClusterParameterInvocation::invoke(OperationContext* opCtx,
+ const SetClusterParameter& cmd) {
+
+ BSONObj cmdParamObj = cmd.getCommandParameter();
+ BSONElement commandElement = cmdParamObj.firstElement();
+ StringData parameterName = commandElement.fieldName();
+
+ const ServerParameter* serverParameter = _sps->getIfExists(parameterName);
+
+ uassert(6432601,
+ str::stream() << "Unknown Cluster Parameter " << parameterName,
+ serverParameter != nullptr);
+ uassert(6432602,
+ "Cluster parameter value must be an object",
+ BSONType::Object == commandElement.type());
+
+ LogicalTime clusterTime = _dbService.getUpdateClusterTime(opCtx);
+
+ BSONObjBuilder updateBuilder;
+ updateBuilder << "_id" << parameterName << "clusterParameterTime" << clusterTime.toBSON();
+ updateBuilder.appendElements(commandElement.Obj());
+
+ BSONObj query = BSON("_id" << parameterName);
+ BSONObj update = updateBuilder.obj();
+
+ LOGV2(6432603, "Updating cluster parameter on-disk", "clusterParameter"_attr = parameterName);
+ uassertStatusOK(serverParameter->validate(update));
+
+ Status updateResult = _dbService.updateParameterOnDisk(query, update);
+ uassertStatusOK(updateResult);
+}
+
+LogicalTime ClusterParameterDBClientService::getUpdateClusterTime(OperationContext* opCtx) {
+ VectorClock::VectorTime vt = VectorClock::get(opCtx)->getTime();
+ return vt.clusterTime();
+}
+
+Status ClusterParameterDBClientService::updateParameterOnDisk(BSONObj query, BSONObj update) {
+ BSONObj res;
+
+ BSONObjBuilder set;
+ set.append("$set", update);
+ set.doneFast();
+
+ const std::string configDb = "config";
+ const auto kMajorityWriteConcern = BSON("writeConcern" << BSON("w"
+ << "majority"));
+ const NamespaceString kClusterParametersNamespace(configDb, "clusterParameters");
+
+ try {
+ _dbClient.runCommand(
+ configDb,
+ [&] {
+ write_ops::UpdateCommandRequest updateOp(kClusterParametersNamespace);
+ updateOp.setUpdates({[&] {
+ write_ops::UpdateOpEntry entry;
+ entry.setQ(query);
+ entry.setU(write_ops::UpdateModification::parseFromClassicUpdate(update));
+ entry.setMulti(false);
+ entry.setUpsert(true);
+ return entry;
+ }()});
+
+ return updateOp.toBSON(kMajorityWriteConcern);
+ }(),
+ res);
+ } catch (const DBException& ex) {
+ return ex.toStatus();
+ }
+
+ BatchedCommandResponse response;
+ std::string errmsg;
+
+ if (!response.parseBSON(res, &errmsg)) {
+ return Status(ErrorCodes::FailedToParse, errmsg);
+ }
+
+ return Status::OK();
+}
+
+const ServerParameter* ClusterParameterService::getIfExists(StringData name) {
+ return ServerParameterSet::getClusterParameterSet()->getIfExists(name);
+}
+} // namespace mongo
diff --git a/src/mongo/db/commands/set_cluster_parameter_invocation.h b/src/mongo/db/commands/set_cluster_parameter_invocation.h
new file mode 100644
index 00000000000..6077ccac5d9
--- /dev/null
+++ b/src/mongo/db/commands/set_cluster_parameter_invocation.h
@@ -0,0 +1,79 @@
+/**
+ * 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/db/commands/set_cluster_parameter_gen.h"
+#include "mongo/db/dbdirectclient.h"
+#include "mongo/db/dbhelpers.h"
+
+namespace mongo {
+
+class ServerParameterService {
+public:
+ virtual const ServerParameter* getIfExists(StringData parameterName) = 0;
+ virtual ~ServerParameterService() = default;
+};
+
+class ClusterParameterService final : public ServerParameterService {
+public:
+ const ServerParameter* getIfExists(StringData parameterName) override;
+};
+
+class DBClientService {
+public:
+ virtual Status updateParameterOnDisk(BSONObj query, BSONObj update) = 0;
+ virtual LogicalTime getUpdateClusterTime(OperationContext*) = 0;
+ virtual ~DBClientService() = default;
+};
+
+class ClusterParameterDBClientService final : public DBClientService {
+public:
+ ClusterParameterDBClientService(DBDirectClient& dbDirectClient) : _dbClient(dbDirectClient) {}
+ Status updateParameterOnDisk(BSONObj query, BSONObj update) override;
+ LogicalTime getUpdateClusterTime(OperationContext*) override;
+
+private:
+ DBDirectClient& _dbClient;
+};
+
+class SetClusterParameterInvocation {
+public:
+ SetClusterParameterInvocation(std::unique_ptr<ServerParameterService> serverParmeterService,
+ DBClientService& dbClientService)
+ : _sps(std::move(serverParmeterService)), _dbService(dbClientService) {}
+
+ void invoke(OperationContext*, const SetClusterParameter&);
+
+private:
+ std::unique_ptr<ServerParameterService> _sps;
+ DBClientService& _dbService;
+
+ Status updateParameterOnDisk(BSONObj query, BSONObj update);
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/commands/set_cluster_parameter_invocation_test.cpp b/src/mongo/db/commands/set_cluster_parameter_invocation_test.cpp
new file mode 100644
index 00000000000..3934e890684
--- /dev/null
+++ b/src/mongo/db/commands/set_cluster_parameter_invocation_test.cpp
@@ -0,0 +1,281 @@
+/**
+ * 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.
+ */
+#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kDefault
+
+#include "mongo/db/operation_context.h"
+#include "mongo/db/operation_id.h"
+#include "mongo/db/service_context.h"
+#include "mongo/platform/basic.h"
+#include <functional>
+
+#include "mongo/db/dbdirectclient.h"
+#include "mongo/logv2/log.h"
+#include "mongo/unittest/unittest.h"
+
+#include "mongo/db/commands/set_cluster_parameter_invocation.h"
+#include "mongo/idl/idl_parser.h"
+
+namespace mongo {
+namespace {
+
+// Mocks
+class MockParameterService : public ServerParameterService {
+public:
+ MockParameterService(std::function<ServerParameter*(StringData)> getIfExists)
+ : getIfExistsMock(getIfExists){};
+
+ const ServerParameter* getIfExists(StringData parameterName) {
+ return getIfExistsMock(parameterName);
+ }
+
+private:
+ std::function<ServerParameter*(StringData)> getIfExistsMock;
+};
+
+class MockServerParameter : public ServerParameter {
+public:
+ MockServerParameter(StringData name,
+ std::function<Status(const BSONElement& newValueElement)> validateImpl)
+ : ServerParameter(name, ServerParameterType::kRuntimeOnly) {
+ this->validateImpl = validateImpl;
+ }
+ void append(OperationContext* opCtx, BSONObjBuilder& b, const std::string& name) {}
+
+ void appendSupportingRoundtrip(OperationContext* opCtx,
+ BSONObjBuilder& b,
+ const std::string& name) {}
+
+ Status set(const BSONElement& newValueElement) {
+ return Status(ErrorCodes::BadValue, "Should not call set() in this test");
+ }
+
+ Status setFromString(const std::string& str) {
+ return Status(ErrorCodes::BadValue, "Should not call setFromString in this test");
+ }
+
+ Status validate(const BSONElement& newValueElement) const {
+ return validateImpl(newValueElement);
+ }
+
+private:
+ std::function<Status(const BSONElement& newValueElement)> validateImpl;
+};
+
+class DBClientMock : public DBClientService {
+public:
+ DBClientMock(std::function<Status(BSONObj, BSONObj)> updateParameterOnDiskMock) {
+ this->updateParameterOnDiskMockImpl = updateParameterOnDiskMock;
+ }
+
+ Status updateParameterOnDisk(BSONObj cmd, BSONObj info) override {
+ return updateParameterOnDiskMockImpl(cmd, info);
+ }
+
+ LogicalTime getUpdateClusterTime(OperationContext*) override {
+ LogicalTime lt;
+ return lt;
+ }
+
+private:
+ std::function<Status(BSONObj, BSONObj)> updateParameterOnDiskMockImpl;
+};
+
+MockServerParameter alwaysValidatingServerParameter(StringData name) {
+
+ MockServerParameter sp(name, [&](const BSONElement& newValueElement) { return Status::OK(); });
+
+ return sp;
+}
+
+MockServerParameter alwaysInvalidatingServerParameter(StringData name) {
+
+ MockServerParameter sp(name, [&](const BSONElement& newValueElement) {
+ return Status(ErrorCodes::BadValue, "Parameter Validation Failed");
+ });
+
+ return sp;
+}
+
+DBClientMock alwaysSucceedingDbClient() {
+ DBClientMock dbServiceMock([&](BSONObj cmd, BSONObj info) { return Status::OK(); });
+
+ return dbServiceMock;
+}
+
+DBClientMock alwaysFailingDbClient() {
+ DBClientMock dbServiceMock([&](BSONObj cmd, BSONObj info) {
+ return Status(ErrorCodes::UnknownError, "DB Client Update Failed");
+ });
+
+ return dbServiceMock;
+}
+
+// Tests
+TEST(SetClusterParameterCommand, SucceedsWithObjectParameter) {
+
+ MockServerParameter sp = alwaysValidatingServerParameter("Succeeds"_sd);
+ DBClientMock dbServiceMock = alwaysSucceedingDbClient();
+
+ 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;
+
+ BSONObj subObj = BSON("ok"
+ << "hello_there");
+ testCmdBson.append("testCmd"_sd, subObj);
+
+ BSONObj obj = testCmdBson.obj();
+
+ SetClusterParameterInvocation fixture(std::move(mpsPtr), dbServiceMock);
+
+ OperationContext spyCtx(clientPtr, 1234);
+ spyCtx.setWriteConcern(WriteConcernOptions::deserializerForIDL(BSON("w"
+ << "majority")));
+ SetClusterParameter testCmd(obj);
+
+ fixture.invoke(&spyCtx, testCmd);
+}
+
+TEST(SetClusterParameterCommand, ThrowsWithNonObjectParameter) {
+
+ MockServerParameter sp = alwaysValidatingServerParameter("Succeeds"_sd);
+ DBClientMock dbServiceMock = alwaysSucceedingDbClient();
+
+ 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" << 5;
+
+ BSONObj obj = testCmdBson.obj();
+
+ SetClusterParameterInvocation fixture(std::move(mpsPtr), dbServiceMock);
+
+ OperationContext spyCtx(clientPtr, 1234);
+ SetClusterParameter testCmd(obj);
+
+ ASSERT_THROWS_CODE(fixture.invoke(&spyCtx, testCmd), DBException, 6432602);
+}
+
+TEST(SetClusterParameterCommand, ThrowsWhenServerParameterValidationFails) {
+
+ MockServerParameter sp =
+ alwaysInvalidatingServerParameter("CommandFailsWhenServerParameterValidationFails"_sd);
+ DBClientMock dbServiceMock = alwaysSucceedingDbClient();
+
+ 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);
+ SetClusterParameter testCmd(obj);
+
+ ASSERT_THROWS_CODE_AND_WHAT(fixture.invoke(&spyCtx, testCmd),
+ DBException,
+ ErrorCodes::BadValue,
+ "Parameter Validation Failed"_sd);
+}
+
+TEST(SetClusterParameterCommand, ThrowsWhenDBUpdateFails) {
+
+ MockServerParameter sp = alwaysValidatingServerParameter("CommandFailsWhenDBUpdateFails"_sd);
+ DBClientMock dbServiceMock = alwaysFailingDbClient();
+
+ 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);
+
+ SetClusterParameter testCmd(obj);
+
+ ASSERT_THROWS_WHAT(fixture.invoke(&spyCtx, testCmd), DBException, "DB Client Update Failed"_sd);
+}
+
+TEST(SetClusterParameterCommand, ThrowsWhenParameterNotPresent) {
+
+ DBClientMock dbServiceMock = alwaysSucceedingDbClient();
+
+ auto serviceCtx = ServiceContext::make();
+ auto client = serviceCtx->makeClient("SomeTest");
+
+ auto mpsPtr = std::make_unique<MockParameterService>([&](StringData s) { return nullptr; });
+
+ 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);
+
+ SetClusterParameter testCmd(obj);
+
+ ASSERT_THROWS_CODE(fixture.invoke(&spyCtx, testCmd), DBException, 6432601);
+}
+} // namespace
+} // namespace mongo