diff options
author | Billy Donahue <billy.donahue@mongodb.com> | 2018-02-27 15:14:14 -0500 |
---|---|---|
committer | Billy Donahue <billy.donahue@mongodb.com> | 2018-03-02 16:31:01 -0500 |
commit | ad94e51e0dd40b0d0c38215a36caf75a4be48415 (patch) | |
tree | 8f621add05d506f88c23e64260780919e1f2d60e /src/mongo/db/commands.cpp | |
parent | ae20f392b61ddc90d2191856e76940ca3c7a3ed2 (diff) | |
download | mongo-ad94e51e0dd40b0d0c38215a36caf75a4be48415.tar.gz |
SERVER-33065 CommandReplyBuilder and CommandInvocation
remove publicRun from mr_test.cpp
change explain to take OpMsgRequest
private explain
private allowsAfterClusterTime
private supportsWriteConcern supportsReadConcern
remove publicRun
cluster_explain_cmd.cpp: do not inject "$db" field.
let explain() exceptions escape
update cluster distinct explain
Diffstat (limited to 'src/mongo/db/commands.cpp')
-rw-r--r-- | src/mongo/db/commands.cpp | 169 |
1 files changed, 127 insertions, 42 deletions
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp index 2aaeda9b5ed..0d8170618d4 100644 --- a/src/mongo/db/commands.cpp +++ b/src/mongo/db/commands.cpp @@ -71,6 +71,23 @@ const WriteConcernOptions kMajorityWriteConcern( WriteConcernOptions::SyncMode::UNSET, Seconds(60)); +// A facade presenting CommandDefinition as an audit::CommandInterface. +class CommandAuditHook : public audit::CommandInterface { +public: + explicit CommandAuditHook(Command* command) : _command(command) {} + + void redactForLogging(mutablebson::Document* cmdObj) const final { + _command->redactForLogging(cmdObj); + } + + std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const final { + return _command->parseNs(dbname, cmdObj); + } + +private: + Command* _command; +}; + } // namespace @@ -80,18 +97,22 @@ const WriteConcernOptions kMajorityWriteConcern( BSONObj CommandHelpers::runCommandDirectly(OperationContext* opCtx, const OpMsgRequest& request) { auto command = globalCommandRegistry()->findCommand(request.getCommandName()); invariant(command); - BSONObjBuilder out; + BufBuilder bb; + CommandReplyBuilder crb(BSONObjBuilder{bb}); try { - bool ok = command->publicRun(opCtx, request, out); - appendCommandStatus(out, ok); + auto invocation = command->parse(opCtx, request); + invocation->run(opCtx, &crb); + auto body = crb.getBodyBuilder(); + CommandHelpers::extractOrAppendOk(body); } catch (const StaleConfigException&) { // These exceptions are intended to be handled at a higher level. throw; } catch (const DBException& ex) { - out.resetToEmpty(); - appendCommandStatus(out, ex.toStatus()); + auto body = crb.getBodyBuilder(); + body.resetToEmpty(); + appendCommandStatus(body, ex.toStatus()); } - return out.obj(); + return BSONObj(bb.release()); } std::string CommandHelpers::parseNsFullyQualified(StringData dbname, const BSONObj& cmdObj) { @@ -167,6 +188,16 @@ void CommandHelpers::appendCommandStatus(BSONObjBuilder& result, } } +bool CommandHelpers::extractOrAppendOk(BSONObjBuilder& reply) { + if (auto okField = reply.asTempObj()["ok"]) { + // If ok is present, use its truthiness. + return okField.trueValue(); + } + // Missing "ok" field is an implied success. + CommandHelpers::appendCommandStatus(reply, true); + return true; +} + void CommandHelpers::appendCommandWCStatus(BSONObjBuilder& result, const Status& awaitReplicationStatus, const WriteConcernResult& wcResult) { @@ -306,10 +337,99 @@ bool CommandHelpers::isHelpRequest(const BSONElement& helpElem) { constexpr StringData CommandHelpers::kHelpFieldName; ////////////////////////////////////////////////////////////// +// CommandReplyBuilder + +CommandReplyBuilder::CommandReplyBuilder(BSONObjBuilder bodyObj) + : _bodyBuf(&bodyObj.bb()), _bodyOffset(bodyObj.offset()) { + // CommandReplyBuilder requires that bodyObj build into an externally-owned buffer. + invariant(!bodyObj.owned()); + bodyObj.doneFast(); +} + +BSONObjBuilder CommandReplyBuilder::getBodyBuilder() const { + return BSONObjBuilder(BSONObjBuilder::ResumeBuildingTag{}, *_bodyBuf, _bodyOffset); +} + +void CommandReplyBuilder::reset() { + getBodyBuilder().resetToEmpty(); +} + +////////////////////////////////////////////////////////////// +// CommandInvocation + +CommandInvocation::~CommandInvocation() = default; + +////////////////////////////////////////////////////////////// // Command +class Command::InvocationShim final : public CommandInvocation { +public: + InvocationShim(OperationContext*, const OpMsgRequest& request, Command* command) + : CommandInvocation(command), + _command(command), + _request(&request), + _dbName(_request->getDatabase().toString()) {} + +private: + void run(OperationContext* opCtx, CommandReplyBuilder* result) override { + try { + BSONObjBuilder bob = result->getBodyBuilder(); + bool ok = _command->enhancedRun(opCtx, *_request, bob); + CommandHelpers::appendCommandStatus(bob, ok); + } catch (const ExceptionFor<ErrorCodes::Unauthorized>&) { + CommandAuditHook hook(_command); + audit::logCommandAuthzCheck( + opCtx->getClient(), *_request, &hook, ErrorCodes::Unauthorized); + throw; + } + } + + void explain(OperationContext* opCtx, + ExplainOptions::Verbosity verbosity, + BSONObjBuilder* result) override { + uassertStatusOK(_command->explain(opCtx, *_request, verbosity, result)); + } + + NamespaceString ns() const override { + return NamespaceString(_command->parseNs(_dbName, cmdObj())); + } + + bool supportsWriteConcern() const override { + return _command->supportsWriteConcern(cmdObj()); + } + + Command::AllowedOnSecondary secondaryAllowed(ServiceContext* context) const override { + return _command->secondaryAllowed(context); + } + + bool supportsReadConcern(repl::ReadConcernLevel level) const override { + return _command->supportsReadConcern(_dbName, cmdObj(), level); + } + + bool allowsAfterClusterTime() const override { + return _command->allowsAfterClusterTime(cmdObj()); + } + + void doCheckAuthorization(OperationContext* opCtx) const override { + uassertStatusOK(_command->checkAuthForRequest(opCtx, *_request)); + } + + const BSONObj& cmdObj() const { + return _request->body; + } + + Command* const _command; + const OpMsgRequest* const _request; + const std::string _dbName; +}; + Command::~Command() = default; +std::unique_ptr<CommandInvocation> Command::parse(OperationContext* opCtx, + const OpMsgRequest& request) { + return stdx::make_unique<InvocationShim>(opCtx, request, this); +} + std::string Command::parseNs(const std::string& dbname, const BSONObj& cmdObj) const { BSONElement first = cmdObj.firstElement(); if (first.type() != mongo::String) @@ -335,8 +455,7 @@ Command::Command(StringData name, StringData oldName) } Status Command::explain(OperationContext* opCtx, - const std::string& dbname, - const BSONObj& cmdObj, + const OpMsgRequest& request, ExplainOptions::Verbosity verbosity, BSONObjBuilder* out) const { return {ErrorCodes::IllegalOperation, str::stream() << "Cannot explain cmd: " << getName()}; @@ -396,25 +515,6 @@ static Status _checkAuthorizationImpl(Command* c, return Status::OK(); } -namespace { -// A facade presenting CommandDefinition as an audit::CommandInterface. -class CommandAuditHook : public audit::CommandInterface { -public: - explicit CommandAuditHook(Command* command) : _command(command) {} - - void redactForLogging(mutablebson::Document* cmdObj) const final { - _command->redactForLogging(cmdObj); - } - - std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const final { - return _command->parseNs(dbname, cmdObj); - } - -private: - Command* _command; -}; -} // namespace - Status Command::checkAuthorization(Command* c, OperationContext* opCtx, const OpMsgRequest& request) { @@ -427,21 +527,6 @@ Status Command::checkAuthorization(Command* c, return status; } -bool Command::publicRun(OperationContext* opCtx, - const OpMsgRequest& request, - BSONObjBuilder& result) { - try { - return enhancedRun(opCtx, request, result); - } catch (const DBException& e) { - if (e.code() == ErrorCodes::Unauthorized) { - CommandAuditHook hook(this); - audit::logCommandAuthzCheck( - opCtx->getClient(), request, &hook, ErrorCodes::Unauthorized); - } - throw; - } -} - void Command::generateHelpResponse(OperationContext* opCtx, rpc::ReplyBuilderInterface* replyBuilder, const Command& command) { |