1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
/**
* Test that a user may currentOp, and then killOp their own operations.
*
* Theory of operation: Create a long running operation from a user which does not have the killOp
* or inProg privileges. Using the same user, run currentOp to get the opId, and then run killOp
* against it.
*/
(function() {
'use strict';
function runTest(m) {
var db = m.getDB("foo");
var admin = m.getDB("admin");
admin.createUser({user: 'admin', pwd: 'password', roles: jsTest.adminUserRoles});
admin.auth('admin', 'password');
db.createUser({user: 'reader', pwd: 'reader', roles: [{db: 'foo', role: 'read'}]});
db.createUser(
{user: 'otherReader', pwd: 'otherReader', roles: [{db: 'foo', role: 'read'}]});
admin.createRole({
role: 'opAdmin',
roles: [],
privileges: [{resource: {cluster: true}, actions: ['inprog', 'killop']}]
});
db.createUser({user: 'opAdmin', pwd: 'opAdmin', roles: [{role: 'opAdmin', db: 'admin'}]});
var t = db.jstests_killop;
t.save({x: 1});
assert.commandWorked(
db.adminCommand({setParameter: 1, internalQueryExecYieldIterations: 1}));
admin.logout();
/**
* This function filters for the operations that we're looking for, based on their state and
* the contents of their query object.
*/
function ops(ownOps = true) {
var p = db.currentOp({$ownOps: ownOps}).inprog;
var ids = [];
for (var i in p) {
var o = p[i];
// We *can't* check for ns, b/c it's not guaranteed to be there unless the query is
// active, which it may not be in our polling cycle - particularly b/c we sleep
// every
// second in both the query and the assert
if ((o.active || o.waitingForLock) && o.query && o.query &&
o.query.find === "jstests_killop" && o.query.comment === "kill_own_ops") {
print("OP: " + tojson(o));
ids.push(o.opid);
}
}
return ids;
}
var queryAsReader =
'db = db.getSiblingDB("foo"); db.auth("reader", "reader"); db.jstests_killop.find().comment("kill_own_ops").toArray()';
jsTestLog("Starting long-running operation");
db.auth('reader', 'reader');
assert.commandWorked(
db.adminCommand({configureFailPoint: "setYieldAllLocksHang", mode: "alwaysOn"}));
var s1 = startParallelShell(queryAsReader, m.port);
jsTestLog("Finding ops in currentOp() output");
var o = [];
assert.soon(
function() {
o = ops();
return o.length == 1;
},
{
toString: function() {
return tojson(db.currentOp().inprog);
}
},
60000);
jsTestLog("Checking that another user cannot see or kill the op");
db.logout();
db.auth('otherReader', 'otherReader');
assert.eq([], ops());
assert.commandFailed(db.killOp(o[0]));
db.logout();
db.auth('reader', 'reader');
assert.eq(1, ops().length);
db.logout();
jsTestLog("Checking that originating user can kill operation");
var start = new Date();
db.auth('reader', 'reader');
assert.commandWorked(db.killOp(o[0]));
assert.commandWorked(
db.adminCommand({configureFailPoint: "setYieldAllLocksHang", mode: "off"}));
jsTestLog("Waiting for ops to terminate");
var exitCode = s1({checkExitSuccess: false});
assert.neq(0,
exitCode,
"expected shell to exit abnormally due to operation execution being terminated");
// don't want to pass if timeout killed the js function.
var end = new Date();
var diff = end - start;
assert.lt(diff, 30000, "Start: " + start + "; end: " + end + "; diff: " + diff);
jsTestLog("Starting a second long-running operation");
assert.commandWorked(
db.adminCommand({configureFailPoint: "setYieldAllLocksHang", mode: "alwaysOn"}));
var s2 = startParallelShell(queryAsReader, m.port);
jsTestLog("Finding ops in currentOp() output");
var o2 = [];
assert.soon(
function() {
o2 = ops();
return o2.length == 1;
},
{
toString: function() {
return tojson(db.currentOp().inprog);
}
},
60000);
db.logout();
db.auth('opAdmin', 'opAdmin');
jsTestLog("Checking that an administrative user can find others' operations");
assert.eq(o2, ops(false));
jsTestLog(
"Checking that an administrative user cannot find others' operations with ownOps");
assert.eq([], ops());
jsTestLog("Checking that an administrative user can kill others' operations");
var start = new Date();
assert.commandWorked(db.killOp(o2[0]));
assert.commandWorked(
db.adminCommand({configureFailPoint: "setYieldAllLocksHang", mode: "off"}));
jsTestLog("Waiting for ops to terminate");
var exitCode = s2({checkExitSuccess: false});
assert.neq(
0, exitCode, "expected shell to exit abnormally due to JS execution being terminated");
var end = new Date();
var diff = end - start;
assert.lt(diff, 30000, "Start: " + start + "; end: " + end + "; diff: " + diff);
}
var m = MongoRunner.runMongod({auth: ""});
runTest(m);
MongoRunner.stopMongod(m);
// TODO: This feature is currently not supported on sharded clusters.
/*var st =
new ShardingTest({shards: 2, config: 3, keyFile: 'jstests/libs/key1', useHostname: false});
runTest(st.s);
st.stop();*/
})();
|