Makes all JS tests access the replica set member state from the class itself instead of the object instance. Also removes some unused code.
339 lines
11 KiB
JavaScript
339 lines
11 KiB
JavaScript
// Tests administrative sharding operations and map-reduce work or fail as expected, when key-based
|
|
// authentication is used
|
|
(function() {
|
|
'use strict';
|
|
|
|
var adminUser = {
|
|
db : "admin",
|
|
username : "foo",
|
|
password : "bar"
|
|
};
|
|
|
|
var testUser = {
|
|
db : "test",
|
|
username : "bar",
|
|
password : "baz"
|
|
};
|
|
|
|
var testUserReadOnly = {
|
|
db : "test",
|
|
username : "sad",
|
|
password : "bat"
|
|
};
|
|
|
|
function login(userObj, thingToUse) {
|
|
if (!thingToUse) {
|
|
thingToUse = s;
|
|
}
|
|
|
|
thingToUse.getDB(userObj.db).auth(userObj.username, userObj.password);
|
|
}
|
|
|
|
function logout(userObj, thingToUse) {
|
|
if (!thingToUse)
|
|
thingToUse = s;
|
|
|
|
s.getDB(userObj.db).runCommand({logout:1});
|
|
}
|
|
|
|
function getShardName(rsTest) {
|
|
var master = rsTest.getPrimary();
|
|
var config = master.getDB("local").system.replset.findOne();
|
|
var members = config.members.map(function(elem) { return elem.host; });
|
|
return config._id+"/"+members.join(",");
|
|
}
|
|
|
|
var s = new ShardingTest({ name: "auth",
|
|
mongos: 1,
|
|
shards: 0,
|
|
other: {
|
|
extraOptions: { "keyFile": "jstests/libs/key1" },
|
|
noChunkSize: true, }
|
|
});
|
|
|
|
if (s.getDB('admin').runCommand('buildInfo').bits < 64) {
|
|
print('Skipping test on 32-bit platforms');
|
|
return;
|
|
}
|
|
|
|
print("Configuration: Add user " + tojson(adminUser));
|
|
s.getDB(adminUser.db).createUser({user: adminUser.username,
|
|
pwd: adminUser.password,
|
|
roles: jsTest.adminUserRoles});
|
|
login(adminUser);
|
|
|
|
// Set the chunk size, disable the secondary throttle (so the test doesn't run so slow)
|
|
assert.writeOK(s.getDB( "config" ).settings.update({ _id: "chunksize" },
|
|
{ $set: { value : 1 } },
|
|
{ upsert: true }));
|
|
assert.writeOK(s.getDB( "config" ).settings.update(
|
|
{ _id: "balancer" },
|
|
{ $set: { "_secondaryThrottle" : false,
|
|
"_waitForDelete" : true } },
|
|
{ upsert: true }));
|
|
|
|
printjson(s.getDB("config").settings.find().toArray());
|
|
|
|
print("Restart mongos with different auth options");
|
|
s.restartMongos(0, { v: 2,
|
|
configdb: s._configDB,
|
|
keyFile: "jstests/libs/key1",
|
|
chunkSize: 1 });
|
|
login(adminUser);
|
|
|
|
var d1 = new ReplSetTest({ name : "d1", nodes : 3, useHostName : true });
|
|
d1.startSet({keyFile : "jstests/libs/key2" });
|
|
d1.initiate();
|
|
|
|
print("d1 initiated");
|
|
var shardName = authutil.asCluster(d1.nodes,
|
|
"jstests/libs/key2",
|
|
function() { return getShardName(d1); });
|
|
|
|
print("adding shard w/out auth "+shardName);
|
|
logout(adminUser);
|
|
|
|
var result = s.getDB("admin").runCommand({addShard : shardName});
|
|
printjson(result);
|
|
assert.eq(result.code, 13);
|
|
|
|
login(adminUser);
|
|
|
|
print("adding shard w/wrong key "+shardName);
|
|
|
|
var thrown = false;
|
|
try {
|
|
result = s.adminCommand({addShard : shardName});
|
|
}
|
|
catch(e) {
|
|
thrown = true;
|
|
printjson(e);
|
|
}
|
|
assert(thrown);
|
|
|
|
print("start rs w/correct key");
|
|
|
|
d1.stopSet();
|
|
d1.startSet({keyFile : "jstests/libs/key1" });
|
|
d1.initiate();
|
|
|
|
var master = d1.getPrimary();
|
|
|
|
print("adding shard w/auth " + shardName);
|
|
|
|
result = s.getDB("admin").runCommand({addShard : shardName});
|
|
assert.eq(result.ok, 1, tojson(result));
|
|
|
|
s.getDB("admin").runCommand({enableSharding : "test"});
|
|
s.getDB("admin").runCommand({shardCollection : "test.foo", key : {x : 1}});
|
|
|
|
d1.waitForState( d1.getSecondaries(), ReplSetTest.State.SECONDARY, 5 * 60 * 1000 )
|
|
|
|
s.getDB(testUser.db).createUser({user: testUser.username,
|
|
pwd: testUser.password,
|
|
roles: jsTest.basicUserRoles})
|
|
s.getDB(testUserReadOnly.db).createUser({user: testUserReadOnly.username,
|
|
pwd: testUserReadOnly.password,
|
|
roles: jsTest.readOnlyUserRoles});
|
|
|
|
logout(adminUser);
|
|
|
|
print("query try");
|
|
var e = assert.throws(function() {
|
|
s.s.getDB("foo").bar.findOne();
|
|
});
|
|
printjson(e);
|
|
|
|
print("cmd try");
|
|
assert.eq(0, s.s.getDB("foo").runCommand({listDatabases:1}).ok);
|
|
|
|
print("insert try 1");
|
|
s.getDB("test").foo.insert({x:1});
|
|
|
|
login(testUser);
|
|
assert.eq(s.getDB("test").foo.findOne(), null);
|
|
|
|
print("insert try 2");
|
|
assert.writeOK(s.getDB("test").foo.insert({ x: 1 }));
|
|
assert.eq( 1 , s.getDB( "test" ).foo.find().itcount() , tojson(result) );
|
|
|
|
logout(testUser);
|
|
|
|
var d2 = new ReplSetTest({name : "d2", nodes : 3, useHostName : true });
|
|
d2.startSet({keyFile : "jstests/libs/key1" });
|
|
d2.initiate();
|
|
d2.awaitSecondaryNodes();
|
|
|
|
shardName = authutil.asCluster(d2.nodes, "jstests/libs/key1",
|
|
function() { return getShardName(d2); });
|
|
|
|
print("adding shard "+shardName);
|
|
login(adminUser);
|
|
print("logged in");
|
|
result = s.getDB("admin").runCommand({addShard : shardName})
|
|
|
|
ReplSetTest.awaitRSClientHosts(s.s, d1.nodes, {ok: true });
|
|
ReplSetTest.awaitRSClientHosts(s.s, d2.nodes, {ok: true });
|
|
|
|
s.getDB("test").foo.remove({})
|
|
|
|
var num = 10000;
|
|
var bulk = s.getDB("test").foo.initializeUnorderedBulkOp();
|
|
for (i=0; i<num; i++) {
|
|
bulk.insert({ _id: i, x: i, abc: "defg", date: new Date(), str: "all the talk on the market" });
|
|
}
|
|
assert.writeOK(bulk.execute());
|
|
|
|
s.startBalancer(60000);
|
|
|
|
assert.soon(function() {
|
|
var d1Chunks = s.getDB("config").chunks.count({shard : "d1"});
|
|
var d2Chunks = s.getDB("config").chunks.count({shard : "d2"});
|
|
var totalChunks = s.getDB("config").chunks.count({ns : "test.foo"});
|
|
|
|
print("chunks: " + d1Chunks+" "+d2Chunks+" "+totalChunks);
|
|
|
|
return d1Chunks > 0 && d2Chunks > 0 && (d1Chunks + d2Chunks == totalChunks);
|
|
},
|
|
"Chunks failed to balance",
|
|
60000,
|
|
5000);
|
|
|
|
//SERVER-3645
|
|
//assert.eq(s.getDB("test").foo.count(), num+1);
|
|
var numDocs = s.getDB("test").foo.find().itcount()
|
|
if (numDocs != num) {
|
|
// Missing documents. At this point we're already in a failure mode, the code in this statement
|
|
// is to get a better idea how/why it's failing.
|
|
|
|
var numDocsSeen = 0;
|
|
var lastDocNumber = -1;
|
|
var missingDocNumbers = [];
|
|
var docs = s.getDB("test").foo.find().sort({x:1}).toArray();
|
|
for (var i = 0; i < docs.length; i++) {
|
|
if (docs[i].x != lastDocNumber + 1) {
|
|
for (var missing = lastDocNumber + 1; missing < docs[i].x; missing++) {
|
|
missingDocNumbers.push(missing);
|
|
}
|
|
}
|
|
lastDocNumber = docs[i].x;
|
|
numDocsSeen++;
|
|
}
|
|
assert.eq(numDocs, numDocsSeen, "More docs discovered on second find()")
|
|
assert.eq(num - numDocs, missingDocNumbers.length);
|
|
|
|
load('jstests/libs/trace_missing_docs.js');
|
|
|
|
for ( var i = 0; i < missingDocNumbers.length; i++ ) {
|
|
jsTest.log( "Tracing doc: " + missingDocNumbers[i] );
|
|
traceMissingDoc( s.getDB( "test" ).foo, { _id : missingDocNumbers[i],
|
|
x : missingDocNumbers[i] } );
|
|
}
|
|
|
|
assert(false, "Number of docs found does not equal the number inserted. Missing docs: " + missingDocNumbers);
|
|
}
|
|
|
|
// We're only sure we aren't duplicating documents iff there's no balancing going on here
|
|
// This call also waits for any ongoing balancing to stop
|
|
s.stopBalancer(60000);
|
|
|
|
var cursor = s.getDB("test").foo.find({x:{$lt : 500}});
|
|
|
|
var count = 0;
|
|
while (cursor.hasNext()) {
|
|
cursor.next();
|
|
count++;
|
|
}
|
|
|
|
assert.eq(count, 500);
|
|
|
|
logout(adminUser);
|
|
|
|
d1.waitForState( d1.getSecondaries(), ReplSetTest.State.SECONDARY, 5 * 60 * 1000 );
|
|
d2.waitForState( d2.getSecondaries(), ReplSetTest.State.SECONDARY, 5 * 60 * 1000 );
|
|
|
|
authutil.asCluster(d1.nodes, "jstests/libs/key1", function() { d1.awaitReplication(120000); });
|
|
authutil.asCluster(d2.nodes, "jstests/libs/key1", function() { d2.awaitReplication(120000); });
|
|
|
|
// add admin on shard itself, hack to prevent localhost auth bypass
|
|
d1.getPrimary().getDB(adminUser.db).createUser({user: adminUser.username,
|
|
pwd: adminUser.password,
|
|
roles: jsTest.adminUserRoles},
|
|
{w: 3, wtimeout: 60000});
|
|
d2.getPrimary().getDB(adminUser.db).createUser({user: adminUser.username,
|
|
pwd: adminUser.password,
|
|
roles: jsTest.adminUserRoles},
|
|
{w: 3, wtimeout: 60000});
|
|
|
|
login(testUser);
|
|
print( "testing map reduce" );
|
|
|
|
// Sharded map reduce can be tricky since all components talk to each other. For example
|
|
// SERVER-4114 is triggered when 1 mongod connects to another for final reduce it's not
|
|
// properly tested here since addresses are localhost, which is more permissive.
|
|
var res = s.getDB("test").runCommand(
|
|
{mapreduce : "foo",
|
|
map : function() { emit(this.x, 1); },
|
|
reduce : function(key, values) { return values.length; },
|
|
out:"mrout"
|
|
});
|
|
printjson(res);
|
|
assert.commandWorked(res);
|
|
|
|
// Check that dump doesn't get stuck with auth
|
|
var x = runMongoProgram("mongodump",
|
|
"--host", s.s.host,
|
|
"-d", testUser.db,
|
|
"-u", testUser.username,
|
|
"-p", testUser.password,
|
|
"--authenticationMechanism", "SCRAM-SHA-1");
|
|
print("result: " + x);
|
|
|
|
// Test read only users
|
|
print( "starting read only tests" );
|
|
|
|
var readOnlyS = new Mongo( s.getDB( "test" ).getMongo().host )
|
|
var readOnlyDB = readOnlyS.getDB( "test" );
|
|
|
|
print( " testing find that should fail" );
|
|
assert.throws( function(){ readOnlyDB.foo.findOne(); } )
|
|
|
|
print( " logging in" );
|
|
login( testUserReadOnly , readOnlyS );
|
|
|
|
print( " testing find that should work" );
|
|
readOnlyDB.foo.findOne();
|
|
|
|
print( " testing write that should fail" );
|
|
assert.writeError(readOnlyDB.foo.insert({ eliot: 1 }));
|
|
|
|
print( " testing read command (should succeed)" );
|
|
assert.commandWorked(readOnlyDB.runCommand({count : "foo"}));
|
|
|
|
print("make sure currentOp/killOp fail");
|
|
assert.commandFailed(readOnlyDB.currentOp());
|
|
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)" );
|
|
assert.commandFailed(readOnlyDB.runCommand(
|
|
{mapreduce : "foo",
|
|
map : function() { emit(this.y, 1); },
|
|
reduce : function(key, values) { return values.length; },
|
|
out:"blarg"
|
|
}));
|
|
*/
|
|
|
|
print( " testing logout (should succeed)" );
|
|
assert.commandWorked(readOnlyDB.runCommand({logout : 1}));
|
|
|
|
print("make sure currentOp/killOp fail again");
|
|
assert.commandFailed(readOnlyDB.currentOp());
|
|
assert.commandFailed(readOnlyDB.killOp(123));
|
|
|
|
s.stop();
|
|
|
|
})();
|