summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/auth/auth3.js2
-rw-r--r--jstests/auth/basic_role_auth.js3
-rw-r--r--jstests/auth/lib/commands_lib.js33
-rw-r--r--jstests/auth/pseudo_commands.js45
-rw-r--r--jstests/multiVersion/kill_op_pseudocommand.js68
-rw-r--r--jstests/sharding/auth.js9
-rw-r--r--src/mongo/SConscript8
-rw-r--r--src/mongo/client/dbclient.cpp38
-rw-r--r--src/mongo/client/dbclientinterface.h20
-rw-r--r--src/mongo/db/SConscript1
-rw-r--r--src/mongo/db/commands.cpp4
-rw-r--r--src/mongo/db/commands/kill_op.cpp91
-rw-r--r--src/mongo/db/instance.cpp40
-rw-r--r--src/mongo/db/repl/SConscript4
-rw-r--r--src/mongo/db/repl/fetcher.cpp2
-rw-r--r--src/mongo/db/repl/freshness_checker.cpp2
-rw-r--r--src/mongo/db/repl/network_interface_impl.cpp2
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp2
-rw-r--r--src/mongo/s/commands/SConscript1
-rw-r--r--src/mongo/s/commands/cluster_kill_op.cpp121
-rw-r--r--src/mongo/s/strategy.cpp47
-rw-r--r--src/mongo/shell/db.js10
-rw-r--r--src/mongo/shell/shell_utils.cpp7
-rw-r--r--src/mongo/util/net/SConscript14
-rw-r--r--src/mongo/util/net/get_status_from_command_result.cpp (renamed from src/mongo/db/get_status_from_command_result.cpp)2
-rw-r--r--src/mongo/util/net/get_status_from_command_result.h (renamed from src/mongo/db/get_status_from_command_result.h)0
26 files changed, 443 insertions, 133 deletions
diff --git a/jstests/auth/auth3.js b/jstests/auth/auth3.js
index a148b936153..b22c86b87b4 100644
--- a/jstests/auth/auth3.js
+++ b/jstests/auth/auth3.js
@@ -13,7 +13,7 @@ assert.eq(x.err, "unauthorized", tojson(x));
x = admin.killOp(123);
assert(!("info" in x), tojson(x));
-assert.eq(x.err, "unauthorized", tojson(x));
+assert.eq(x.code, errorCodeUnauthorized, tojson(x));
x = admin.fsyncUnlock();
assert(x.errmsg != "not locked", tojson(x));
diff --git a/jstests/auth/basic_role_auth.js b/jstests/auth/basic_role_auth.js
index 95b18050116..1122951fd25 100644
--- a/jstests/auth/basic_role_auth.js
+++ b/jstests/auth/basic_role_auth.js
@@ -149,9 +149,10 @@ var testOps = function(db, allowedActions) {
});
checkErr(allowedActions.hasOwnProperty('killOp'), function() {
+ var errorCodeUnauthorized = 13;
var res = db.killOp(1);
- if (res.err == 'unauthorized') {
+ if (res.code == errorCodeUnauthorized) {
throw Error("unauthorized killOp");
}
});
diff --git a/jstests/auth/lib/commands_lib.js b/jstests/auth/lib/commands_lib.js
index 8e8f22b2363..25827201154 100644
--- a/jstests/auth/lib/commands_lib.js
+++ b/jstests/auth/lib/commands_lib.js
@@ -1463,6 +1463,39 @@ var authCommandsLib = {
]
},
{
+ testname: "killOp", // standalone version
+ command: {killOp: 1, op: 123},
+ skipSharded: true,
+ testcases : [
+ {
+ runOnDb: adminDbName,
+ roles: roles_hostManager,
+ privileges: [
+ { resource: {cluster: true}, actions: ["killop"] }
+ ]
+ },
+ { runOnDb: firstDbName, roles: {} },
+ { runOnDb: secondDbName, roles: {} }
+ ]
+ },
+ {
+ testname: "killOp", // sharded version
+ command: {killOp: 1, op: "shard1:123"},
+ skipStandalone: true,
+ testcases : [
+ {
+ runOnDb: adminDbName,
+ roles: roles_hostManager,
+ privileges: [
+ { resource: {cluster: true}, actions: ["killop"] }
+ ],
+ expectFail: true // we won't be able to find the shardId
+ },
+ { runOnDb: firstDbName, roles: {} },
+ { runOnDb: secondDbName, roles: {} }
+ ]
+ },
+ {
testname: "listCommands",
command: {listCommands: 1},
testcases: [
diff --git a/jstests/auth/pseudo_commands.js b/jstests/auth/pseudo_commands.js
index 17cbf25a8e1..006d84e147d 100644
--- a/jstests/auth/pseudo_commands.js
+++ b/jstests/auth/pseudo_commands.js
@@ -119,59 +119,16 @@ function runTest(conn) {
}
var res = db.killOp(opid);
printjson(res);
- passed = !res.err && !res['$err'];
+ passed = res.ok && !res.errmsg && !res.err && !res['$err'];
} catch (e) {
passed = false;
}
-
assert.eq(shouldPass, passed);
}
testProperAuthorization(testFunc, roles, privilege);
})();
- (function testKillop() {
- jsTestLog("Testing killOp");
-
- var roles = {read: false,
- readAnyDatabase: false,
- readWrite: false,
- readWriteAnyDatabase: false,
- dbAdmin: false,
- dbAdminAnyDatabase: false,
- dbOwner: false,
- clusterMonitor: false,
- clusterManager: false,
- hostManager: true,
- clusterAdmin: true,
- root: true,
- __system: true
- };
-
- var privilege = { resource: {cluster: true}, actions: ['killop'] };
-
- var testFunc = function(shouldPass) {
- var passed = true;
- try {
- var opid;
- if (isMongos(db)) { // opid format different between mongos and mongod
- opid = "shard0000:1234";
- } else {
- opid = 1234;
- }
- var res = db.killOp(opid);
- printjson(res);
- passed = !res.err && !res['$err'];
- } catch (e) {
- passed = false;
- }
-
- assert.eq(shouldPass, passed);
- }
-
- testProperAuthorization(testFunc, roles, privilege);
- })();
-
(function testUnlock() {
if (isMongos(db)) {
return; // unlock doesn't work on mongos
diff --git a/jstests/multiVersion/kill_op_pseudocommand.js b/jstests/multiVersion/kill_op_pseudocommand.js
new file mode 100644
index 00000000000..b5acd4ad774
--- /dev/null
+++ b/jstests/multiVersion/kill_op_pseudocommand.js
@@ -0,0 +1,68 @@
+(function() {
+ "use strict";
+ // Test that a 3.2 Mongos will correctly translate a killOp command
+ // to a killop pseudocommand when talking to an old shard
+
+ // TODO: Remove after mongodb 3.2 is released
+
+ // Sharded cluster
+ // -- latest mongos
+ // -- latest config
+ // -- one 3.0 shard
+ var options = {
+ mongosOptions: {binVersion: "3.1"},
+ configOptions: {binVersion: "3.1"},
+ shardOptions: {binVersion: "3.0"}
+ };
+
+ var st = new ShardingTest({name: "killOp-multiver", shards: 1, other: options});
+
+ var db = st.s.getDB("killOp-multiver");
+ var db1 = db;
+ db.dropDatabase();
+ assert.commandWorked(db.adminCommand({enableSharding: db.getName()}));
+
+ var testCol = db.tc;
+
+ // start long running op
+ testCol.insert({"foo": "bar"});
+ jsTestLog("Starting long-running $where operation");
+
+ var start = new Date();
+
+ var parShell = startParallelShell(
+ 'db.getSiblingDB("killOp-multiver").tc.count( { $where: function() { while( 1 ) { ; } }})',
+ st.s.port);
+ var findOpId = function () {
+ var curOps = db.currentOp();
+ var inProg = curOps.inprog;
+ var opId = null;
+ inProg.forEach(function(op) {
+ if ((op.active === true) &&
+ (op.ns === "killOp-multiver.tc") &&
+ (op.query.count === "tc")) {
+
+ opId = op.opid;
+ }
+ });
+ return opId;
+ };
+
+ var opToKill = null;
+
+ do {
+ opToKill = findOpId()
+ sleep(25);
+ } while (opToKill === null);
+
+ db.killOp(opToKill);
+ try {
+ parShell(); // wait for query to end
+ } catch (ex) {} // ignore
+
+ var end = new Date();
+
+ // make sure the query didn't end due to js op timeout
+ assert.lt(diff, 30000, "Query was killed due to timeout - not killOp");
+ st.stop();
+})();
diff --git a/jstests/sharding/auth.js b/jstests/sharding/auth.js
index 41783fcce1a..3de61fafa0d 100644
--- a/jstests/sharding/auth.js
+++ b/jstests/sharding/auth.js
@@ -294,11 +294,8 @@ function runTest(s) {
assert.throws(function() {
printjson(readOnlyDB.currentOp());
});
- assert.throws(function() {
- printjson(readOnlyDB.killOp(123));
- });
+ assert.commandFailed(readOnlyDB.killOp(123));
// fsyncUnlock doesn't work in mongos anyway, so no need check authorization for it
-
/*
broken because of SERVER-4156
print( " testing write command (should fail)" );
@@ -316,9 +313,7 @@ function runTest(s) {
assert.throws(function() {
printjson(readOnlyDB.currentOp());
});
- assert.throws(function() {
- printjson(readOnlyDB.killOp(123));
- });
+ assert.commandFailed(readOnlyDB.killOp(123));
// fsyncUnlock doesn't work in mongos anyway, so no need check authorization for it
}
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index f281d1cca66..7303b035560 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -57,7 +57,11 @@ env.SConscript(['base/SConscript',
'util/concurrency/SConscript',
'util/options_parser/SConscript',
'util/cmdline_utils/SConscript',
- 'util/mongoutils/SConscript'])
+ 'util/mongoutils/SConscript',
+ 'util/net/SConscript',
+ ]
+)
+
def add_exe( v ):
return "${PROGPREFIX}%s${PROGSUFFIX}" % v
@@ -342,6 +346,7 @@ env.Library('clientdriver', [
],
LIBDEPS=['$BUILD_DIR/mongo/db/auth/authcommon',
'$BUILD_DIR/mongo/crypto/scramauth',
+ '$BUILD_DIR/mongo/util/net/command_status',
'network'
])
@@ -753,6 +758,7 @@ serverOnlyFiles = [ "db/background.cpp",
"db/commands/getmore_cmd.cpp",
"db/commands/group.cpp",
"db/commands/index_filter_commands.cpp",
+ "db/commands/kill_op.cpp",
"db/commands/list_collections.cpp",
"db/commands/list_databases.cpp",
"db/commands/list_indexes.cpp",
diff --git a/src/mongo/client/dbclient.cpp b/src/mongo/client/dbclient.cpp
index be4f1653811..df6b4c39fae 100644
--- a/src/mongo/client/dbclient.cpp
+++ b/src/mongo/client/dbclient.cpp
@@ -47,6 +47,7 @@
#include "mongo/util/assert_util.h"
#include "mongo/util/debug_util.h"
#include "mongo/util/log.h"
+#include "mongo/util/net/get_status_from_command_result.h"
#include "mongo/util/net/ssl_manager.h"
#include "mongo/util/net/ssl_options.h"
#include "mongo/util/password_digest.h"
@@ -478,6 +479,43 @@ namespace mongo {
return runCommand(dbname, b.done(), *info);
}
+ bool DBClientWithCommands::runPseudoCommand(StringData db,
+ StringData realCommandName,
+ StringData pseudoCommandCol,
+ const BSONObj& cmdArgs,
+ BSONObj& info,
+ int options) {
+
+ BSONObjBuilder bob;
+ bob.append(realCommandName, 1);
+ bob.appendElements(cmdArgs);
+ auto cmdObj = bob.done();
+
+ bool success = false;
+
+ if (!(success = runCommand(db.toString(), cmdObj, info, options))) {
+
+ auto status = getStatusFromCommandResult(info);
+ verify(!status.isOK());
+
+ if (status == ErrorCodes::CommandResultSchemaViolation) {
+ msgasserted(28624, str::stream() << "Received bad "
+ << realCommandName
+ << " response from server: "
+ << info);
+ } else if (status == ErrorCodes::CommandNotFound ||
+ str::startsWith(status.reason(), "no such")) {
+
+ NamespaceString pseudoCommandNss(db, pseudoCommandCol);
+ // if this throws we just let it escape as that's how runCommand works.
+ info = findOne(pseudoCommandNss.ns(), cmdArgs, nullptr, options);
+ return true;
+ }
+ }
+
+ return success;
+ }
+
unsigned long long DBClientWithCommands::count(const string &myns, const BSONObj& query, int options, int limit, int skip ) {
BSONObj cmd = _countCmd( myns , query , options , limit , skip );
BSONObj res;
diff --git a/src/mongo/client/dbclientinterface.h b/src/mongo/client/dbclientinterface.h
index 87c7f69f37d..e2d6bafe0d8 100644
--- a/src/mongo/client/dbclientinterface.h
+++ b/src/mongo/client/dbclientinterface.h
@@ -41,6 +41,7 @@
#include "mongo/db/jsobj.h"
#include "mongo/logger/log_severity.h"
#include "mongo/platform/atomic_word.h"
+#include "mongo/platform/cstdint.h"
#include "mongo/stdx/functional.h"
#include "mongo/util/mongoutils/str.h"
#include "mongo/util/net/message.h"
@@ -847,7 +848,6 @@ namespace mongo {
bool setDbProfilingLevel(const std::string &dbname, ProfilingLevel level, BSONObj *info = 0);
bool getDbProfilingLevel(const std::string &dbname, ProfilingLevel& level, BSONObj *info = 0);
-
/** This implicitly converts from char*, string, and BSONObj to be an argument to mapreduce
You shouldn't need to explicitly construct this
*/
@@ -1035,6 +1035,24 @@ namespace mongo {
return _postRunCommandHook;
}
+ /**
+ * Run a pseudo-command such as sys.inprog/currentOp, sys.killop/killOp
+ * or sys.unlock/fsyncUnlock
+ *
+ * The real command will be tried first, and if the remote server does not
+ * implement the command, it will fall back to the pseudoCommand.
+ *
+ * The cmdArgs parameter should NOT include {<commandName>: 1}.
+ *
+ * TODO: remove after MongoDB 3.2 is released and replace all callers with
+ * a call to plain runCommand
+ */
+ virtual bool runPseudoCommand(StringData db,
+ StringData realCommandName,
+ StringData pseudoCommandCol,
+ const BSONObj& cmdArgs,
+ BSONObj& info,
+ int options=0);
protected:
/** if the result of a command is ok*/
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 299c671fa51..c0bc6f93a08 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -14,7 +14,6 @@ env.Library(
'field_ref.cpp',
'field_ref_set.cpp',
'field_parser.cpp',
- 'get_status_from_command_result.cpp',
'keypattern.cpp',
'write_concern_options.cpp'
],
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index 8278d91fcad..45a84e3452e 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -38,8 +38,8 @@
#include <string>
#include <vector>
-#include "mongo/bson/mutable/document.h"
#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/bson/mutable/document.h"
#include "mongo/db/audit.h"
#include "mongo/db/auth/action_set.h"
#include "mongo/db/auth/action_type.h"
@@ -47,12 +47,12 @@
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/client.h"
-#include "mongo/db/get_status_from_command_result.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/server_parameters.h"
#include "mongo/s/write_ops/wc_error_detail.h"
#include "mongo/util/log.h"
+#include "mongo/util/net/get_status_from_command_result.h"
namespace mongo {
diff --git a/src/mongo/db/commands/kill_op.cpp b/src/mongo/db/commands/kill_op.cpp
new file mode 100644
index 00000000000..e8f80928558
--- /dev/null
+++ b/src/mongo/db/commands/kill_op.cpp
@@ -0,0 +1,91 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * 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 GNU Affero General 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_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand
+
+#include "mongo/platform/basic.h"
+
+#include <limits>
+
+#include "mongo/base/init.h"
+#include "mongo/base/status.h"
+#include "mongo/bson/util/bson_extract.h"
+#include "mongo/db/audit.h"
+#include "mongo/db/auth/authorization_session.h"
+#include "mongo/db/client.h"
+#include "mongo/db/commands.h"
+#include "mongo/db/service_context.h"
+#include "mongo/util/log.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+
+ class KillOpCommand : public Command {
+ public:
+
+ KillOpCommand() : Command("killOp") {}
+
+ bool isWriteCommandForConfigServer() const final { return false; }
+
+ bool slaveOk() const final { return true; }
+
+ bool adminOnly() const final { return true; }
+
+ Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) final {
+
+ bool isAuthorized = client->getAuthorizationSession()->isAuthorizedForActionsOnResource(
+ ResourcePattern::forClusterResource(),
+ ActionType::killop);
+ return isAuthorized ? Status::OK() : Status(ErrorCodes::Unauthorized, "Unauthorized");
+ }
+
+ bool run(OperationContext* txn,
+ const std::string& db,
+ BSONObj& cmdObj,
+ int options,
+ std::string& errmsg,
+ BSONObjBuilder& result,
+ bool fromRepl) final {
+
+ long long op;
+ uassertStatusOK(bsonExtractIntegerField(cmdObj, "op", &op));
+
+ log() << "going to kill op: " << op;
+ result.append("info", "attempting to kill op");
+
+ uassert(26823, str::stream() << "invalid op : " << op,
+ (op >= 0) && (op <= std::numeric_limits<unsigned int>::max()));
+
+ getGlobalServiceContext()->killOperation(static_cast<unsigned int>(op));
+ return true;
+ }
+ } killOpCmd;
+
+} // namespace mongo
diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp
index 2ab4f1e58a3..f5efc2938d4 100644
--- a/src/mongo/db/instance.cpp
+++ b/src/mongo/db/instance.cpp
@@ -150,36 +150,6 @@ namespace mongo {
MONGO_FP_DECLARE(rsStopGetMore);
- void killOp( OperationContext* txn, Message &m, DbResponse &dbresponse ) {
- DbMessage d(m);
- QueryMessage q(d);
- BSONObj obj;
-
- const bool isAuthorized = txn->getClient()->getAuthorizationSession()->isAuthorizedForActionsOnResource(
- ResourcePattern::forClusterResource(), ActionType::killop);
- audit::logKillOpAuthzCheck(txn->getClient(),
- q.query,
- isAuthorized ? ErrorCodes::OK : ErrorCodes::Unauthorized);
- if (!isAuthorized) {
- obj = fromjson("{\"err\":\"unauthorized\"}");
- }
- /*else if( !dbMutexInfo.isLocked() )
- obj = fromjson("{\"info\":\"no op in progress/not locked\"}");
- */
- else {
- BSONElement e = q.query.getField("op");
- if( !e.isNumber() ) {
- obj = fromjson("{\"err\":\"no op number field specified?\"}");
- }
- else {
- log() << "going to kill op: " << e << endl;
- obj = fromjson("{\"info\":\"attempting to kill op\"}");
- getGlobalServiceContext()->killOperation( (unsigned) e.number() );
- }
- }
- replyToQuery(0, m, dbresponse, obj);
- }
-
namespace {
void generateErrorResponse(const AssertionException* exception,
@@ -310,12 +280,18 @@ namespace {
DbResponse& dbResponse,
Message& message,
StringData realCommandName) {
- Message interposed;
+ DbMessage originalDbm(message);
+ originalDbm.pullInt(); // ntoskip
+ originalDbm.pullInt(); // ntoreturn
+ auto cmdParams = originalDbm.nextJsObj();
+
+ Message interposed;
NamespaceString interposedNss(nss.db(), "$cmd");
BSONObjBuilder cmdBob;
cmdBob.append(realCommandName, 1);
+ cmdBob.appendElements(cmdParams);
auto cmd = cmdBob.done();
// TODO: use OP_COMMAND here instead of constructing
@@ -416,7 +392,7 @@ namespace {
return;
}
if (nsString.coll() == "$cmd.sys.killop") {
- killOp(txn, m, dbresponse);
+ receivedPseudoCommand(txn, nsString, c, dbresponse, m, "killOp");
return;
}
if (nsString.coll() == "$cmd.sys.unlock") {
diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript
index f6fa162790b..e8d797785de 100644
--- a/src/mongo/db/repl/SConscript
+++ b/src/mongo/db/repl/SConscript
@@ -90,10 +90,10 @@ env.Library('repl_coordinator_impl',
'$BUILD_DIR/mongo/db/common',
'$BUILD_DIR/mongo/db/index/index_descriptor',
'$BUILD_DIR/mongo/fail_point',
- '$BUILD_DIR/mongo/foundation',
'$BUILD_DIR/mongo/global_optime',
'$BUILD_DIR/mongo/server_options_core',
'$BUILD_DIR/mongo/service_context',
+ '$BUILD_DIR/mongo/util/net/command_status',
'repl_coordinator_interface',
'replica_set_messages',
'replication_executor',
@@ -230,9 +230,9 @@ env.Library(
],
LIBDEPS=[
'replication_executor',
- '$BUILD_DIR/mongo/db/common',
'$BUILD_DIR/mongo/logger/logger',
'$BUILD_DIR/mongo/namespace_string',
+ '$BUILD_DIR/mongo/util/net/command_status',
],
)
diff --git a/src/mongo/db/repl/fetcher.cpp b/src/mongo/db/repl/fetcher.cpp
index ab0198a9363..d691fe4bca6 100644
--- a/src/mongo/db/repl/fetcher.cpp
+++ b/src/mongo/db/repl/fetcher.cpp
@@ -30,12 +30,12 @@
#include "mongo/db/repl/fetcher.h"
-#include "mongo/db/get_status_from_command_result.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/repl/replication_executor.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/mongoutils/str.h"
+#include "mongo/util/net/get_status_from_command_result.h"
namespace mongo {
namespace repl {
diff --git a/src/mongo/db/repl/freshness_checker.cpp b/src/mongo/db/repl/freshness_checker.cpp
index 62e514c6793..3f533e28f6e 100644
--- a/src/mongo/db/repl/freshness_checker.cpp
+++ b/src/mongo/db/repl/freshness_checker.cpp
@@ -34,12 +34,12 @@
#include "mongo/base/status.h"
#include "mongo/bson/optime.h"
-#include "mongo/db/get_status_from_command_result.h"
#include "mongo/db/repl/member_heartbeat_data.h"
#include "mongo/db/repl/replica_set_config.h"
#include "mongo/db/repl/replication_executor.h"
#include "mongo/db/repl/scatter_gather_runner.h"
#include "mongo/util/log.h"
+#include "mongo/util/net/get_status_from_command_result.h"
#include "mongo/util/scopeguard.h"
#include "mongo/util/time_support.h"
diff --git a/src/mongo/db/repl/network_interface_impl.cpp b/src/mongo/db/repl/network_interface_impl.cpp
index 998fe7016ee..8a7f50e6bbe 100644
--- a/src/mongo/db/repl/network_interface_impl.cpp
+++ b/src/mongo/db/repl/network_interface_impl.cpp
@@ -42,7 +42,6 @@
#include "mongo/db/auth/internal_user_auth.h"
#include "mongo/db/client.h"
#include "mongo/db/concurrency/d_concurrency.h"
-#include "mongo/db/get_status_from_command_result.h"
#include "mongo/db/operation_context_impl.h"
#include "mongo/db/repl/network_interface_impl_downconvert_find_getmore.h"
#include "mongo/platform/unordered_map.h"
@@ -50,6 +49,7 @@
#include "mongo/stdx/list.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/log.h"
+#include "mongo/util/net/get_status_from_command_result.h"
#include "mongo/util/net/hostandport.h"
#include "mongo/util/time_support.h"
diff --git a/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp b/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp
index 2479d831247..9ec647d8dee 100644
--- a/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp
@@ -33,7 +33,6 @@
#include <algorithm>
#include "mongo/base/status.h"
-#include "mongo/db/get_status_from_command_result.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/repl/elect_cmd_runner.h"
#include "mongo/db/repl/freshness_checker.h"
@@ -49,6 +48,7 @@
#include "mongo/util/fail_point_service.h"
#include "mongo/util/log.h"
#include "mongo/util/mongoutils/str.h"
+#include "mongo/util/net/get_status_from_command_result.h"
#include "mongo/util/time_support.h"
namespace mongo {
diff --git a/src/mongo/s/commands/SConscript b/src/mongo/s/commands/SConscript
index 0a2f7b19a3c..3d359c9e357 100644
--- a/src/mongo/s/commands/SConscript
+++ b/src/mongo/s/commands/SConscript
@@ -20,6 +20,7 @@ env.Library(
'cluster_index_filter_cmd.cpp',
'cluster_is_db_grid_cmd.cpp',
'cluster_is_master_cmd.cpp',
+ 'cluster_kill_op.cpp',
'cluster_list_databases_cmd.cpp',
'cluster_list_shards_cmd.cpp',
'cluster_merge_chunks_cmd.cpp',
diff --git a/src/mongo/s/commands/cluster_kill_op.cpp b/src/mongo/s/commands/cluster_kill_op.cpp
new file mode 100644
index 00000000000..88f43fd88d9
--- /dev/null
+++ b/src/mongo/s/commands/cluster_kill_op.cpp
@@ -0,0 +1,121 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * 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 GNU Affero General 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_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand
+
+#include "mongo/platform/basic.h"
+
+#include <string>
+
+#include "mongo/bson/bsonelement.h"
+#include "mongo/bson/bsonobj.h"
+#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/bson/util/bson_extract.h"
+#include "mongo/client/connpool.h"
+#include "mongo/client/dbclientinterface.h"
+#include "mongo/db/audit.h"
+#include "mongo/db/auth/authorization_session.h"
+#include "mongo/db/commands.h"
+#include "mongo/s/shard.h"
+#include "mongo/util/log.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+namespace {
+
+ class ClusterKillOpCommand : public Command {
+ public:
+ ClusterKillOpCommand() : Command("killOp") {}
+
+ bool isWriteCommandForConfigServer() const final { return false; }
+
+ bool slaveOk() const final { return true; }
+
+ bool adminOnly() const final { return true; }
+
+ Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) final {
+
+ bool isAuthorized = client->getAuthorizationSession()->isAuthorizedForActionsOnResource(
+ ResourcePattern::forClusterResource(),
+ ActionType::killop);
+ return isAuthorized ? Status::OK() : Status(ErrorCodes::Unauthorized, "Unauthorized");
+ }
+
+ bool run(OperationContext* txn,
+ const std::string& db,
+ BSONObj& cmdObj,
+ int options,
+ std::string& errmsg,
+ BSONObjBuilder& result,
+ bool fromRepl) final {
+
+ // The format of op is shardid:opid
+ // This is different than the format passed to the mongod killOp command.
+ std::string opToKill;
+ uassertStatusOK(bsonExtractStringField(cmdObj, "op", &opToKill));
+
+ auto opSepPos = opToKill.find(':');
+
+ uassert(28625,
+ str::stream() << "The op argument to killOp must be of the format shardid:opid"
+ << " but found \"" << opToKill << '"',
+ (opToKill.size() >= 3) &&// must have at least N:N
+ (opSepPos != std::string::npos) && // must have ':' as separator
+ (opSepPos != 0) && // can't be :NN
+ (opSepPos != (opToKill.size() - 1))); // can't be NN:
+
+ auto shardIdent = opToKill.substr(0, opSepPos);
+ auto opId = std::stoi(opToKill.substr(opSepPos + 1));
+
+ // shardid is actually the opid - keeping for backwards compatibility.
+ result.append("shard" , shardIdent);
+ result.append("shardid", opId);
+
+ log() << "want to kill op: " << opToKill;
+
+ // Will throw if shard id is not found
+ Shard s(shardIdent);
+ ScopedDbConnection conn(s.getConnString());
+ BSONObj cmdRes;
+ BSONObjBuilder argsBob;
+ argsBob.append("op", opId);
+ auto args = argsBob.done();
+ // intentionally ignore return value - that is how legacy killOp worked.
+ conn->runPseudoCommand("admin", "killOp", "$cmd.sys.killop", args, cmdRes);
+ conn.done();
+ // The original behavior of killOp on mongos is to always return success, regardless of
+ // whether the shard reported success or not.
+ return true;
+ }
+
+ } clusterKillOpCommand;
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/s/strategy.cpp b/src/mongo/s/strategy.cpp
index 553d4dc1a2b..3901d11ed9d 100644
--- a/src/mongo/s/strategy.cpp
+++ b/src/mongo/s/strategy.cpp
@@ -330,6 +330,7 @@ namespace mongo {
}
}
+ // TODO: remove after MongoDB 3.2
bool Strategy::handleSpecialNamespaces( Request& r , QueryMessage& q ) {
const char * ns = strstr( r.getns() , ".$cmd.sys." );
if ( ! ns )
@@ -385,40 +386,18 @@ namespace mongo {
arr.done();
}
else if ( strcmp( ns , "killop" ) == 0 ) {
- const bool isAuthorized = authSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forClusterResource(), ActionType::killop);
- audit::logKillOpAuthzCheck(
- client,
- q.query,
- isAuthorized ? ErrorCodes::OK : ErrorCodes::Unauthorized);
- uassert(ErrorCodes::Unauthorized, "not authorized to run killop", isAuthorized);
-
- BSONElement e = q.query["op"];
- if ( e.type() != String ) {
- b.append( "err" , "bad op" );
- b.append( e );
- }
- else {
- b.append( e );
- string s = e.String();
- string::size_type i = s.find( ':' );
- if ( i == string::npos ) {
- b.append( "err" , "bad opid" );
- }
- else {
- string shard = s.substr( 0 , i );
- int opid = atoi( s.substr( i + 1 ).c_str() );
- b.append( "shard" , shard );
- b.append( "shardid" , opid );
-
- log() << "want to kill op: " << e << endl;
- Shard s(shard);
-
- ScopedDbConnection conn(s.getConnString());
- conn->findOne( r.getns() , BSON( "op" << opid ) );
- conn.done();
- }
- }
+ BSONObjBuilder cmdBob;
+ cmdBob.append("killOp", 1);
+ cmdBob.appendElements(q.query); // fields are validated by ClusterKillOpCommand
+ auto interposedCmd = cmdBob.done();
+
+ NamespaceString nss(r.getns());
+ NamespaceString interposedNss(nss.db(), "$cmd");
+
+ Command::runAgainstRegistered(interposedNss.ns().c_str(),
+ interposedCmd,
+ b,
+ q.queryOptions);
}
else if ( strcmp( ns , "unlock" ) == 0 ) {
b.append( "err" , "can't do unlock through mongos" );
diff --git a/src/mongo/shell/db.js b/src/mongo/shell/db.js
index 382b6b33fab..b73e0e9f2e2 100644
--- a/src/mongo/shell/db.js
+++ b/src/mongo/shell/db.js
@@ -698,7 +698,15 @@ DB.prototype.currentOP = DB.prototype.currentOp;
DB.prototype.killOp = function(op) {
if( !op )
throw Error("no opNum to kill specified");
- return this.$cmd.sys.killop.findOne({'op':op});
+ var res = this.adminCommand({'killOp': 1, 'op': op});
+ if (!res.ok &&
+ (res.errmsg.startsWith("no such cmd") ||
+ res.errmsg.startsWith("no such command")) ||
+ res.code === 59) {
+ // fall back for old servers
+ res = this.$cmd.sys.killop.findOne({'op': op});
+ }
+ return res;
}
DB.prototype.killOP = DB.prototype.killOp;
diff --git a/src/mongo/shell/shell_utils.cpp b/src/mongo/shell/shell_utils.cpp
index ebc903e6488..a4895af7bdf 100644
--- a/src/mongo/shell/shell_utils.cpp
+++ b/src/mongo/shell/shell_utils.cpp
@@ -322,7 +322,12 @@ namespace mongo {
BSONForEach( op, inprog ) {
if ( uris.count( op[ "client" ].String() ) ) {
if ( !withPrompt || prompter.confirm() ) {
- conn->findOne( "admin.$cmd.sys.killop", QUERY( "op"<< op[ "opid" ] ) );
+ BSONObjBuilder cmdBob;
+ BSONObj info;
+ cmdBob.append("op", op["opid"]);
+ auto cmdArgs = cmdBob.done();
+ conn->runPseudoCommand("admin", "killOp", "$cmd.sys.killop",
+ cmdArgs, info);
}
else {
return;
diff --git a/src/mongo/util/net/SConscript b/src/mongo/util/net/SConscript
new file mode 100644
index 00000000000..4bd51bed099
--- /dev/null
+++ b/src/mongo/util/net/SConscript
@@ -0,0 +1,14 @@
+# -*- mode: python -*-
+
+Import("env")
+
+
+# TODO(amidvidy) move to OP_COMMAND subdirectory
+env.Library(
+ target='command_status',
+ source='get_status_from_command_result.cpp',
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/bson',
+ '$BUILD_DIR/mongo/foundation'
+ ]
+)
diff --git a/src/mongo/db/get_status_from_command_result.cpp b/src/mongo/util/net/get_status_from_command_result.cpp
index 14c3ffbd25e..febb4a14958 100644
--- a/src/mongo/db/get_status_from_command_result.cpp
+++ b/src/mongo/util/net/get_status_from_command_result.cpp
@@ -28,7 +28,7 @@
#include "mongo/platform/basic.h"
-#include "mongo/db/get_status_from_command_result.h"
+#include "mongo/util/net/get_status_from_command_result.h"
#include "mongo/base/status.h"
#include "mongo/db/jsobj.h"
diff --git a/src/mongo/db/get_status_from_command_result.h b/src/mongo/util/net/get_status_from_command_result.h
index 6c930ccd017..6c930ccd017 100644
--- a/src/mongo/db/get_status_from_command_result.h
+++ b/src/mongo/util/net/get_status_from_command_result.h