617 lines
24 KiB
JavaScript
617 lines
24 KiB
JavaScript
/**
|
|
* Tests that the $currentOp aggregation stage behaves as expected. Specifically:
|
|
* - It must be the first stage in the pipeline.
|
|
* - It can only be run on admin, and the "aggregate" field must be 1.
|
|
* - Only active connections are shown unless {idleConnections: true} is specified.
|
|
* - A user without the inprog privilege can see their own ops, but no-one else's.
|
|
* - A user with the inprog privilege can see all ops.
|
|
* - Non-local readConcerns are rejected.
|
|
* - Collation rules are respected.
|
|
*
|
|
* Also verifies that the aggregation-backed currentOp command obeys the same rules, where
|
|
* applicable.
|
|
*
|
|
* This test requires replica set configuration and user credentials to persist across a restart.
|
|
* @tags: [requires_persistence]
|
|
*/
|
|
(function() {
|
|
"use strict";
|
|
|
|
const key = "jstests/libs/key1";
|
|
|
|
// Create a new sharded cluster for testing. We set the internalQueryExecYieldIterations
|
|
// parameter so that plan execution yields on every iteration. For some tests, we will
|
|
// temporarily set yields to hang the mongod so we can capture particular operations in the
|
|
// currentOp output.
|
|
const st = new ShardingTest({
|
|
name: jsTestName(),
|
|
keyFile: key,
|
|
shards: 3,
|
|
rs: {
|
|
nodes: [
|
|
{rsConfig: {priority: 1}},
|
|
{rsConfig: {priority: 0}},
|
|
{rsConfig: {arbiterOnly: true}}
|
|
],
|
|
setParameter: {internalQueryExecYieldIterations: 1}
|
|
}
|
|
});
|
|
|
|
// Assign various elements of the cluster. We will use shard rs0 to test replica-set level
|
|
// $currentOp behaviour.
|
|
let shardConn = st.rs0.getPrimary();
|
|
const mongosConn = st.s;
|
|
const shardRS = st.rs0;
|
|
|
|
const clusterTestDB = mongosConn.getDB(jsTestName());
|
|
const clusterAdminDB = mongosConn.getDB("admin");
|
|
let shardAdminDB = shardConn.getDB("admin");
|
|
|
|
function createUsers(conn) {
|
|
let adminDB = conn.getDB("admin");
|
|
|
|
// Create an admin user, one user with the inprog privilege, and one without.
|
|
assert.commandWorked(
|
|
adminDB.runCommand({createUser: "admin", pwd: "pwd", roles: ["root"]}));
|
|
assert(adminDB.auth("admin", "pwd"));
|
|
|
|
assert.commandWorked(adminDB.runCommand({
|
|
createRole: "role_inprog",
|
|
roles: [],
|
|
privileges: [{resource: {cluster: true}, actions: ["inprog"]}]
|
|
}));
|
|
|
|
assert.commandWorked(
|
|
adminDB.runCommand({createUser: "user_inprog", pwd: "pwd", roles: ["role_inprog"]}));
|
|
|
|
assert.commandWorked(adminDB.runCommand(
|
|
{createUser: "user_no_inprog", pwd: "pwd", roles: ["readAnyDatabase"]}));
|
|
}
|
|
|
|
// Create necessary users at both cluster and shard-local level.
|
|
createUsers(shardConn);
|
|
createUsers(mongosConn);
|
|
|
|
// Create a test database and some dummy data on rs0.
|
|
assert(clusterAdminDB.auth("admin", "pwd"));
|
|
|
|
for (let i = 0; i < 5; i++) {
|
|
assert.writeOK(clusterTestDB.test.insert({_id: i, a: i}));
|
|
}
|
|
|
|
st.ensurePrimaryShard(clusterTestDB.getName(), shardRS.name);
|
|
|
|
// Restarts a replica set with additional parameters, and optionally re-authenticates.
|
|
function restartReplSet(replSet, newOpts, user, pwd) {
|
|
const numNodes = replSet.nodeList().length;
|
|
|
|
for (let n = 0; n < numNodes; n++) {
|
|
replSet.restart(n, newOpts);
|
|
}
|
|
|
|
shardConn = replSet.getPrimary();
|
|
replSet.awaitSecondaryNodes();
|
|
|
|
shardAdminDB = shardConn.getDB("admin");
|
|
|
|
if (user && pwd) {
|
|
shardAdminDB.auth(user, pwd);
|
|
}
|
|
}
|
|
|
|
// Functions to support running an operation in a parallel shell for testing allUsers behaviour.
|
|
function runInParallelShell({conn, testfunc, username, password}) {
|
|
TestData.aggCurOpTest = testfunc;
|
|
TestData.aggCurOpUser = username;
|
|
TestData.aggCurOpPwd = password;
|
|
|
|
assert.commandWorked(conn.getDB("admin").runCommand(
|
|
{configureFailPoint: "setYieldAllLocksHang", mode: "alwaysOn"}));
|
|
|
|
testfunc = function() {
|
|
db.getSiblingDB("admin").auth(TestData.aggCurOpUser, TestData.aggCurOpPwd);
|
|
TestData.aggCurOpTest();
|
|
db.getSiblingDB("admin").logout();
|
|
};
|
|
|
|
return startParallelShell(testfunc, conn.port);
|
|
}
|
|
|
|
function assertCurrentOpHasSingleMatchingEntry({conn, currentOpAggFilter, curOpOpts}) {
|
|
curOpOpts = (curOpOpts || {allUsers: true});
|
|
|
|
const connAdminDB = conn.getDB("admin");
|
|
|
|
let curOpResult;
|
|
|
|
assert.soon(
|
|
function() {
|
|
curOpResult =
|
|
connAdminDB.aggregate([{$currentOp: curOpOpts}, {$match: currentOpAggFilter}])
|
|
.toArray();
|
|
|
|
return curOpResult.length === 1;
|
|
},
|
|
function() {
|
|
return "Failed to find operation " + tojson(currentOpAggFilter) +
|
|
" in $currentOp output: " + tojson(curOpResult);
|
|
});
|
|
|
|
return curOpResult[0];
|
|
}
|
|
|
|
function waitForParallelShell(conn, awaitShell) {
|
|
assert.commandWorked(conn.getDB("admin").runCommand(
|
|
{configureFailPoint: "setYieldAllLocksHang", mode: "off"}));
|
|
|
|
awaitShell();
|
|
}
|
|
|
|
function getCollectionNameFromFullNamespace(ns) {
|
|
return ns.split(/\.(.+)/)[1];
|
|
}
|
|
|
|
// Generic function for running getMore on a $currentOp aggregation cursor and returning the
|
|
// command response.
|
|
function getMoreTest({conn, showAllUsers, getMoreBatchSize}) {
|
|
// Ensure that there are some other connections present so that the result set is larger
|
|
// than 1 $currentOp entry.
|
|
const otherConns = [new Mongo(conn.host), new Mongo(conn.host)];
|
|
|
|
// Log the other connections in as user_no_inprog so that they will show up for user_inprog
|
|
// with {allUsers: true} and user_no_inprog with {allUsers: false}.
|
|
for (let otherConn of otherConns) {
|
|
assert(otherConn.getDB("admin").auth("user_no_inprog", "pwd"));
|
|
}
|
|
|
|
const connAdminDB = conn.getDB("admin");
|
|
|
|
const aggCmdRes = assert.commandWorked(connAdminDB.runCommand({
|
|
aggregate: 1,
|
|
pipeline: [{$currentOp: {allUsers: showAllUsers, idleConnections: true}}],
|
|
cursor: {batchSize: 0}
|
|
}));
|
|
assert.neq(aggCmdRes.cursor.id, 0);
|
|
|
|
return connAdminDB.runCommand({
|
|
getMore: aggCmdRes.cursor.id,
|
|
collection: getCollectionNameFromFullNamespace(aggCmdRes.cursor.ns),
|
|
batchSize: (getMoreBatchSize || 100)
|
|
});
|
|
}
|
|
|
|
// Runs a suite of tests for behaviour common to both the replica set and cluster levels.
|
|
function runCommonTests(conn) {
|
|
const testDB = conn.getDB(jsTestName());
|
|
const adminDB = conn.getDB("admin");
|
|
|
|
const isMongos = (conn == mongosConn);
|
|
|
|
// Test that an unauthenticated connection cannot run $currentOp even with {allUsers:
|
|
// false}.
|
|
assert(adminDB.logout());
|
|
|
|
assert.commandFailedWithCode(
|
|
adminDB.runCommand(
|
|
{aggregate: 1, pipeline: [{$currentOp: {allUsers: false}}], cursor: {}}),
|
|
ErrorCodes.Unauthorized);
|
|
|
|
// Test that an unauthenticated connection cannot run the currentOp command even with
|
|
// {$ownOps: true}.
|
|
assert.commandFailedWithCode(adminDB.currentOp({$ownOps: true}), ErrorCodes.Unauthorized);
|
|
|
|
//
|
|
// Authenticate as user_no_inprog.
|
|
//
|
|
assert(adminDB.logout());
|
|
assert(adminDB.auth("user_no_inprog", "pwd"));
|
|
|
|
// Test that $currentOp fails with {allUsers: true} for a user without the "inprog"
|
|
// privilege.
|
|
assert.commandFailedWithCode(
|
|
adminDB.runCommand(
|
|
{aggregate: 1, pipeline: [{$currentOp: {allUsers: true}}], cursor: {}}),
|
|
ErrorCodes.Unauthorized);
|
|
|
|
// Test that the currentOp command fails with {ownOps: false} for a user without the
|
|
// "inprog" privilege.
|
|
assert.commandFailedWithCode(adminDB.currentOp({$ownOps: false}), ErrorCodes.Unauthorized);
|
|
|
|
// Test that {aggregate: 1} fails when the first stage in the pipeline is not $currentOp.
|
|
assert.commandFailedWithCode(
|
|
adminDB.runCommand({aggregate: 1, pipeline: [{$match: {}}], cursor: {}}),
|
|
ErrorCodes.InvalidNamespace);
|
|
|
|
//
|
|
// Authenticate as user_inprog.
|
|
//
|
|
assert(adminDB.logout());
|
|
assert(adminDB.auth("user_inprog", "pwd"));
|
|
|
|
// Test that $currentOp fails when it is not the first stage in the pipeline. We use two
|
|
// $currentOp stages since any other stage in the initial position will trip the {aggregate:
|
|
// 1} namespace check.
|
|
assert.commandFailedWithCode(
|
|
adminDB.runCommand(
|
|
{aggregate: 1, pipeline: [{$currentOp: {}}, {$currentOp: {}}], cursor: {}}),
|
|
40602);
|
|
|
|
// Test that $currentOp fails when run on admin without {aggregate: 1}.
|
|
assert.commandFailedWithCode(
|
|
adminDB.runCommand({aggregate: "collname", pipeline: [{$currentOp: {}}], cursor: {}}),
|
|
ErrorCodes.InvalidNamespace);
|
|
|
|
// Test that $currentOp fails when run as {aggregate: 1} on a database other than admin.
|
|
assert.commandFailedWithCode(
|
|
testDB.runCommand({aggregate: 1, pipeline: [{$currentOp: {}}], cursor: {}}),
|
|
ErrorCodes.InvalidNamespace);
|
|
|
|
// Test that the currentOp command fails when run directly on a database other than admin.
|
|
assert.commandFailedWithCode(testDB.runCommand({currentOp: 1}), ErrorCodes.Unauthorized);
|
|
|
|
// Test that the currentOp command helper succeeds when run on a database other than admin.
|
|
// This is because the currentOp shell helper redirects the command to the admin database.
|
|
assert.commandWorked(testDB.currentOp());
|
|
|
|
// Test that $currentOp and the currentOp command accept all numeric types.
|
|
const ones = [1, 1.0, NumberInt(1), NumberLong(1), NumberDecimal(1)];
|
|
|
|
for (let one of ones) {
|
|
assert.commandWorked(
|
|
adminDB.runCommand({aggregate: one, pipeline: [{$currentOp: {}}], cursor: {}}));
|
|
|
|
assert.commandWorked(adminDB.runCommand({currentOp: one, $ownOps: true}));
|
|
}
|
|
|
|
// Test that $currentOp with {allUsers: true} succeeds for a user with the "inprog"
|
|
// privilege.
|
|
assert.commandWorked(adminDB.runCommand(
|
|
{aggregate: 1, pipeline: [{$currentOp: {allUsers: true}}], cursor: {}}));
|
|
|
|
// Test that the currentOp command with {$ownOps: false} succeeds for a user with the
|
|
// "inprog" privilege.
|
|
assert.commandWorked(adminDB.currentOp({$ownOps: false}));
|
|
|
|
// Test that $currentOp succeeds if local readConcern is specified.
|
|
assert.commandWorked(adminDB.runCommand({
|
|
aggregate: 1,
|
|
pipeline: [{$currentOp: {}}],
|
|
readConcern: {level: "local"},
|
|
cursor: {}
|
|
}));
|
|
|
|
// Test that $currentOp fails if a non-local readConcern is specified.
|
|
assert.commandFailedWithCode(adminDB.runCommand({
|
|
aggregate: 1,
|
|
pipeline: [{$currentOp: {}}],
|
|
readConcern: {level: "linearizable"},
|
|
cursor: {}
|
|
}),
|
|
ErrorCodes.InvalidOptions);
|
|
|
|
// Test that {idleConnections: false} returns only active connections.
|
|
const idleConn = new Mongo(conn.host);
|
|
|
|
assert.eq(adminDB
|
|
.aggregate([
|
|
{$currentOp: {allUsers: true, idleConnections: false}},
|
|
{$match: {active: false}}
|
|
])
|
|
.itcount(),
|
|
0);
|
|
|
|
// Test that the currentOp command with {$all: false} returns only active connections.
|
|
assert.eq(adminDB.currentOp({$ownOps: false, $all: false, active: false}).inprog.length, 0);
|
|
|
|
// Test that {idleConnections: true} returns inactive connections.
|
|
assert.gte(adminDB
|
|
.aggregate([
|
|
{$currentOp: {allUsers: true, idleConnections: true}},
|
|
{$match: {active: false}}
|
|
])
|
|
.itcount(),
|
|
1);
|
|
|
|
// Test that the currentOp command with {$all: true} returns inactive connections.
|
|
assert.gte(adminDB.currentOp({$ownOps: false, $all: true, active: false}).inprog.length, 1);
|
|
|
|
// Test that collation rules apply to matches on $currentOp output.
|
|
const matchField = (isMongos ? "originatingCommand.comment" : "command.comment");
|
|
const numExpectedMatches = (isMongos ? 3 : 1);
|
|
|
|
assert.eq(
|
|
adminDB
|
|
.aggregate(
|
|
[{$currentOp: {}}, {$match: {[matchField]: "AGG_currént_op_COLLATION"}}],
|
|
{
|
|
collation: {locale: "en_US", strength: 1}, // Case and diacritic insensitive.
|
|
comment: "agg_current_op_collation"
|
|
})
|
|
.itcount(),
|
|
numExpectedMatches);
|
|
|
|
// Test that $currentOp output can be processed by $facet subpipelines.
|
|
assert.eq(adminDB
|
|
.aggregate(
|
|
[
|
|
{$currentOp: {}},
|
|
{
|
|
$facet: {
|
|
testFacet: [
|
|
{$match: {[matchField]: "agg_current_op_facets"}},
|
|
{$count: "count"}
|
|
]
|
|
}
|
|
},
|
|
{$unwind: "$testFacet"},
|
|
{$replaceRoot: {newRoot: "$testFacet"}}
|
|
],
|
|
{comment: "agg_current_op_facets"})
|
|
.next()
|
|
.count,
|
|
numExpectedMatches);
|
|
|
|
// Test that $currentOp is explainable.
|
|
const explainPlan = assert.commandWorked(adminDB.runCommand({
|
|
aggregate: 1,
|
|
pipeline:
|
|
[{$currentOp: {idleConnections: true, allUsers: false}}, {$match: {desc: "test"}}],
|
|
explain: true
|
|
}));
|
|
|
|
const expectedStages = [
|
|
{$currentOp: {idleConnections: true, allUsers: false, truncateOps: false}},
|
|
{$match: {desc: {$eq: "test"}}}
|
|
];
|
|
|
|
if (isMongos) {
|
|
assert.eq(explainPlan.splitPipeline.shardsPart, expectedStages);
|
|
|
|
for (let i = 0; i < 3; i++) {
|
|
let shardName = st["rs" + i].name;
|
|
assert.eq(explainPlan.shards[shardName].stages, expectedStages);
|
|
}
|
|
} else {
|
|
assert.eq(explainPlan.stages, expectedStages);
|
|
}
|
|
|
|
// Test that a user with the inprog privilege can run getMore on a $currentOp aggregation
|
|
// cursor which they created with {allUsers: true}.
|
|
let getMoreCmdRes = assert.commandWorked(
|
|
getMoreTest({conn: conn, showAllUsers: true, getMoreBatchSize: 1}));
|
|
|
|
// Test that a user without the inprog privilege cannot run getMore on a $currentOp
|
|
// aggregation cursor created by a user with {allUsers: true}.
|
|
assert(adminDB.logout());
|
|
assert(adminDB.auth("user_no_inprog", "pwd"));
|
|
|
|
assert.neq(getMoreCmdRes.cursor.id, 0);
|
|
assert.commandFailedWithCode(adminDB.runCommand({
|
|
getMore: getMoreCmdRes.cursor.id,
|
|
collection: getCollectionNameFromFullNamespace(getMoreCmdRes.cursor.ns),
|
|
batchSize: 100
|
|
}),
|
|
ErrorCodes.Unauthorized);
|
|
}
|
|
|
|
runCommonTests(shardConn);
|
|
runCommonTests(mongosConn);
|
|
|
|
//
|
|
// mongoS specific tests.
|
|
//
|
|
|
|
// Test that a user without the inprog privilege cannot run cluster $currentOp via mongoS even
|
|
// if allUsers is false.
|
|
assert(clusterAdminDB.logout());
|
|
assert(clusterAdminDB.auth("user_no_inprog", "pwd"));
|
|
|
|
assert.commandFailedWithCode(
|
|
clusterAdminDB.runCommand(
|
|
{aggregate: 1, pipeline: [{$currentOp: {allUsers: false}}], cursor: {}}),
|
|
ErrorCodes.Unauthorized);
|
|
|
|
// Test that a user without the inprog privilege cannot run the currentOp command via mongoS
|
|
// even if $ownOps is true.
|
|
assert.commandFailedWithCode(clusterAdminDB.currentOp({$ownOps: true}),
|
|
ErrorCodes.Unauthorized);
|
|
|
|
// Test that a $currentOp pipeline returns results from all shards, and includes both the shard
|
|
// and host names.
|
|
assert(clusterAdminDB.logout());
|
|
assert(clusterAdminDB.auth("user_inprog", "pwd"));
|
|
|
|
assert.eq(clusterAdminDB
|
|
.aggregate([
|
|
{$currentOp: {allUsers: true, idleConnections: true}},
|
|
{$group: {_id: {shard: "$shard", host: "$host"}}},
|
|
{$sort: {_id: 1}}
|
|
])
|
|
.toArray(),
|
|
[
|
|
{_id: {shard: "aggregation_currentop-rs0", host: st.rs0.getPrimary().host}},
|
|
{_id: {shard: "aggregation_currentop-rs1", host: st.rs1.getPrimary().host}},
|
|
{_id: {shard: "aggregation_currentop-rs2", host: st.rs2.getPrimary().host}}
|
|
]);
|
|
|
|
//
|
|
// ReplSet specific tests.
|
|
//
|
|
|
|
// Test that a user with the inprog privilege can see another user's operations with {allUsers:
|
|
// true} when run on a mongoD.
|
|
assert(shardAdminDB.logout());
|
|
assert(shardAdminDB.auth("user_inprog", "pwd"));
|
|
|
|
let awaitShell = runInParallelShell({
|
|
testfunc: function() {
|
|
assert.eq(db.getSiblingDB(jsTestName())
|
|
.test.find({})
|
|
.comment("agg_current_op_allusers_test")
|
|
.itcount(),
|
|
5);
|
|
},
|
|
conn: shardConn,
|
|
username: "admin",
|
|
password: "pwd"
|
|
});
|
|
|
|
assertCurrentOpHasSingleMatchingEntry(
|
|
{conn: shardConn, currentOpAggFilter: {"command.comment": "agg_current_op_allusers_test"}});
|
|
|
|
// Test that the currentOp command can see another user's operations with {$ownOps: false}.
|
|
assert.eq(
|
|
shardAdminDB.currentOp({$ownOps: false, "command.comment": "agg_current_op_allusers_test"})
|
|
.inprog.length,
|
|
1);
|
|
|
|
// Allow the op to complete.
|
|
waitForParallelShell(shardConn, awaitShell);
|
|
|
|
// Test that $currentOp succeeds with {allUsers: false} for a user without the "inprog"
|
|
// privilege when run on a mongoD.
|
|
assert(shardAdminDB.logout());
|
|
assert(shardAdminDB.auth("user_no_inprog", "pwd"));
|
|
|
|
assert.commandWorked(shardAdminDB.runCommand(
|
|
{aggregate: 1, pipeline: [{$currentOp: {allUsers: false}}], cursor: {}}));
|
|
|
|
// Test that the currentOp command succeeds with {$ownOps: true} for a user without the "inprog"
|
|
// privilege when run on a mongoD.
|
|
assert.commandWorked(shardAdminDB.currentOp({$ownOps: true}));
|
|
|
|
// Test that a user without the inprog privilege cannot see another user's operations.
|
|
// Temporarily log in as 'user_inprog' to validate that the op is present in $currentOp output.
|
|
assert(shardAdminDB.logout());
|
|
assert(shardAdminDB.auth("user_inprog", "pwd"));
|
|
|
|
awaitShell = runInParallelShell({
|
|
testfunc: function() {
|
|
assert.eq(db.getSiblingDB(jsTestName())
|
|
.test.find({})
|
|
.comment("agg_current_op_allusers_test")
|
|
.itcount(),
|
|
5);
|
|
},
|
|
conn: shardConn,
|
|
username: "admin",
|
|
password: "pwd"
|
|
});
|
|
|
|
assertCurrentOpHasSingleMatchingEntry({
|
|
currentOpAggFilter: {"command.comment": "agg_current_op_allusers_test"},
|
|
curOpOpts: {allUsers: true},
|
|
conn: shardConn
|
|
});
|
|
|
|
// Log back in as 'user_no_inprog' and validate that the user cannot see the op.
|
|
assert(shardAdminDB.logout());
|
|
assert(shardAdminDB.auth("user_no_inprog", "pwd"));
|
|
|
|
assert.eq(shardAdminDB
|
|
.aggregate([
|
|
{$currentOp: {allUsers: false}},
|
|
{$match: {"command.comment": "agg_current_op_allusers_test"}}
|
|
])
|
|
.itcount(),
|
|
0);
|
|
|
|
// Test that a user without the inprog privilege cannot see another user's operations via the
|
|
// currentOp command.
|
|
assert.eq(
|
|
shardAdminDB.currentOp({$ownOps: true, "command.comment": "agg_current_op_allusers_test"})
|
|
.inprog.length,
|
|
0);
|
|
|
|
waitForParallelShell(shardConn, awaitShell);
|
|
|
|
// Test that a user without the inprog privilege can run getMore on a $currentOp cursor which
|
|
// they created with {allUsers: false}.
|
|
assert.commandWorked(getMoreTest({conn: shardConn, showAllUsers: false}));
|
|
|
|
// Test that the allUsers parameter is ignored when authentication is disabled.
|
|
restartReplSet(shardRS, {shardsvr: null, keyFile: null});
|
|
|
|
// Ensure that there is at least one other connection present.
|
|
const otherConn = new Mongo(shardConn.host);
|
|
|
|
// Verify that $currentOp displays all operations when auth is disabled regardless of the
|
|
// allUsers parameter, by confirming that we can see non-client system operations when
|
|
// {allUsers: false} is specified.
|
|
assert.gte(shardAdminDB
|
|
.aggregate([
|
|
{$currentOp: {allUsers: false, idleConnections: true}},
|
|
{$match: {connectionId: {$exists: false}}}
|
|
])
|
|
.itcount(),
|
|
1);
|
|
|
|
// Verify that the currentOp command displays all operations when auth is disabled regardless of
|
|
// the $ownOps parameter, by confirming that we can see non-client system operations when
|
|
// {$ownOps: true} is specified.
|
|
assert.gte(shardAdminDB.currentOp({$ownOps: true, $all: true, connectionId: {$exists: false}})
|
|
.inprog.length,
|
|
1);
|
|
|
|
// Test that a user can run getMore on a $currentOp cursor when authentication is disabled.
|
|
assert.commandWorked(getMoreTest({conn: shardConn, showAllUsers: true}));
|
|
|
|
// Test that the host field is present and the shard field is absent when run on mongoD.
|
|
assert.eq(shardAdminDB
|
|
.aggregate([
|
|
{$currentOp: {allUsers: true, idleConnections: true}},
|
|
{$group: {_id: {shard: "$shard", host: "$host"}}}
|
|
])
|
|
.toArray(),
|
|
[
|
|
{_id: {host: shardConn.host}},
|
|
]);
|
|
|
|
// Test that attempting to 'spoof' a sharded request on non-shardsvr mongoD fails.
|
|
assert.commandFailedWithCode(
|
|
shardAdminDB.runCommand(
|
|
{aggregate: 1, pipeline: [{$currentOp: {}}], fromMongos: true, cursor: {}}),
|
|
40465);
|
|
|
|
// Test that an operation which is at the BSON user size limit does not throw an error when the
|
|
// currentOp metadata is added to the output document.
|
|
const bsonUserSizeLimit = assert.commandWorked(shardAdminDB.isMaster()).maxBsonObjectSize;
|
|
|
|
let aggPipeline = [
|
|
{$currentOp: {}},
|
|
{
|
|
$match: {
|
|
$or: [
|
|
{
|
|
"command.comment": "agg_current_op_bson_limit_test",
|
|
"command.$truncated": {$exists: false}
|
|
},
|
|
{padding: ""}
|
|
]
|
|
}
|
|
}
|
|
];
|
|
|
|
aggPipeline[1].$match.$or[1].padding =
|
|
"a".repeat(bsonUserSizeLimit - Object.bsonsize(aggPipeline));
|
|
|
|
assert.eq(Object.bsonsize(aggPipeline), bsonUserSizeLimit);
|
|
|
|
assert.eq(
|
|
shardAdminDB.aggregate(aggPipeline, {comment: "agg_current_op_bson_limit_test"}).itcount(),
|
|
1);
|
|
|
|
// Test that $currentOp can run while the mongoD is write-locked.
|
|
awaitShell = startParallelShell(function() {
|
|
assert.commandFailedWithCode(db.adminCommand({sleep: 1, lock: "w", secs: 300}),
|
|
ErrorCodes.Interrupted);
|
|
}, shardConn.port);
|
|
|
|
const op = assertCurrentOpHasSingleMatchingEntry(
|
|
{conn: shardConn, currentOpAggFilter: {"command.sleep": 1, active: true}});
|
|
|
|
assert.commandWorked(shardAdminDB.killOp(op.opid));
|
|
|
|
awaitShell();
|
|
})();
|