301 lines
12 KiB
JavaScript
301 lines
12 KiB
JavaScript
/**
|
|
* This tests using DB commands with authentication enabled when sharded.
|
|
*/
|
|
var doTest = function() {
|
|
|
|
var rsOpts = { oplogSize: 10, useHostname : false };
|
|
var st = new ShardingTest({ keyFile : 'jstests/libs/key1', shards : 2, chunksize : 2,
|
|
rs : rsOpts, other : { nopreallocj : 1, useHostname : false }});
|
|
|
|
var mongos = st.s;
|
|
var adminDB = mongos.getDB( 'admin' );
|
|
var configDB = mongos.getDB( 'config' );
|
|
var testDB = mongos.getDB( 'test' );
|
|
|
|
jsTestLog('Setting up initial users');
|
|
var rwUser = 'rwUser';
|
|
var roUser = 'roUser';
|
|
var password = 'password';
|
|
var expectedDocs = 1000;
|
|
|
|
adminDB.createUser({user: rwUser, pwd: password, roles: jsTest.adminUserRoles});
|
|
|
|
assert( adminDB.auth( rwUser, password ) );
|
|
|
|
// Secondaries should be up here, since we awaitReplication in the ShardingTest, but we *don't*
|
|
// wait for the mongos to explicitly detect them.
|
|
ReplSetTest.awaitRSClientHosts( mongos, st.rs0.getSecondaries(), { ok : true, secondary : true });
|
|
ReplSetTest.awaitRSClientHosts( mongos, st.rs1.getSecondaries(), { ok : true, secondary : true });
|
|
|
|
testDB.createUser({user: rwUser, pwd: password, roles: jsTest.basicUserRoles});
|
|
testDB.createUser({user: roUser, pwd: password, roles: jsTest.readOnlyUserRoles});
|
|
|
|
authenticatedConn = new Mongo( mongos.host );
|
|
authenticatedConn.getDB( 'admin' ).auth( rwUser, password );
|
|
|
|
// Add user to shards to prevent localhost connections from having automatic full access
|
|
st.rs0.getPrimary().getDB( 'admin' ).createUser({user: 'user',
|
|
pwd: 'password',
|
|
roles: jsTest.basicUserRoles},
|
|
{w: 3, wtimeout: 30000});
|
|
st.rs1.getPrimary().getDB( 'admin' ).createUser({user: 'user',
|
|
pwd: 'password',
|
|
roles: jsTest.basicUserRoles},
|
|
{w: 3, wtimeout: 30000} );
|
|
|
|
|
|
|
|
jsTestLog('Creating initial data');
|
|
|
|
st.adminCommand( { enablesharding : "test" } );
|
|
st.ensurePrimaryShard('test', 'test-rs0');
|
|
st.adminCommand( { shardcollection : "test.foo" , key : { i : 1, j : 1 } } );
|
|
|
|
// Stop the balancer, so no moveChunks will interfere with the splits we're testing
|
|
st.stopBalancer()
|
|
|
|
var str = 'a';
|
|
while ( str.length < 8000 ) {
|
|
str += str;
|
|
}
|
|
|
|
var bulk = testDB.foo.initializeUnorderedBulkOp();
|
|
for ( var i = 0; i < 100; i++ ) {
|
|
for ( var j = 0; j < 10; j++ ) {
|
|
bulk.insert({i:i, j:j, str:str});
|
|
}
|
|
}
|
|
assert.writeOK(bulk.execute({ w: "majority"}));
|
|
|
|
assert.eq(expectedDocs, testDB.foo.count());
|
|
|
|
// Wait for the balancer to start back up
|
|
st.startBalancer()
|
|
|
|
// Make sure we've done at least some splitting, so the balancer will work
|
|
assert.gt( configDB.chunks.find({ ns : 'test.foo' }).count(), 2 )
|
|
|
|
// Make sure we eventually balance all the chunks we've created
|
|
assert.soon( function() {
|
|
var x = st.chunkDiff( "foo", "test" );
|
|
print( "chunk diff: " + x );
|
|
return x < 2 && configDB.locks.findOne({ _id : 'test.foo' }).state == 0;
|
|
}, "no balance happened", 5 * 60 * 1000 );
|
|
|
|
assert.soon( function(){
|
|
print( "Waiting for migration cleanup to occur..." )
|
|
return testDB.foo.find().itcount() == testDB.foo.count();
|
|
})
|
|
|
|
var map = function() { emit (this.i, this.j) };
|
|
var reduce = function( key, values ) {
|
|
var jCount = 0;
|
|
values.forEach( function(j) { jCount += j; } );
|
|
return jCount;
|
|
};
|
|
|
|
var checkCommandSucceeded = function( db, cmdObj ) {
|
|
print( "Running command that should succeed: " );
|
|
printjson( cmdObj );
|
|
resultObj = db.runCommand( cmdObj );
|
|
printjson( resultObj )
|
|
assert ( resultObj.ok );
|
|
return resultObj;
|
|
}
|
|
|
|
var checkCommandFailed = function( db, cmdObj ) {
|
|
print( "Running command that should fail: " );
|
|
printjson( cmdObj );
|
|
resultObj = db.runCommand( cmdObj );
|
|
printjson( resultObj )
|
|
assert ( !resultObj.ok );
|
|
return resultObj;
|
|
}
|
|
|
|
var checkReadOps = function( hasReadAuth ) {
|
|
if ( hasReadAuth ) {
|
|
print( "Checking read operations, should work" );
|
|
assert.eq( expectedDocs, testDB.foo.find().itcount() );
|
|
assert.eq( expectedDocs, testDB.foo.count() );
|
|
// NOTE: This is an explicit check that GLE can be run with read prefs, not the result of
|
|
// above.
|
|
assert.eq( null, testDB.runCommand({getlasterror : 1}).err );
|
|
checkCommandSucceeded( testDB, {dbstats : 1} );
|
|
checkCommandSucceeded( testDB, {collstats : 'foo'} );
|
|
|
|
// inline map-reduce works read-only
|
|
var res = checkCommandSucceeded( testDB, {mapreduce : 'foo', map : map, reduce : reduce,
|
|
out : {inline : 1}});
|
|
assert.eq( 100, res.results.length );
|
|
assert.eq( 45, res.results[0].value );
|
|
|
|
res = checkCommandSucceeded( testDB,
|
|
{aggregate:'foo',
|
|
pipeline: [ {$project : {j : 1}},
|
|
{$group : {_id : 'j', sum : {$sum : '$j'}}}]} );
|
|
assert.eq( 4500, res.result[0].sum );
|
|
} else {
|
|
print( "Checking read operations, should fail" );
|
|
assert.throws( function() { testDB.foo.find().itcount(); } );
|
|
checkCommandFailed( testDB, {dbstats : 1} );
|
|
checkCommandFailed( testDB, {collstats : 'foo'} );
|
|
checkCommandFailed( testDB, {mapreduce : 'foo', map : map, reduce : reduce,
|
|
out : { inline : 1 }} );
|
|
checkCommandFailed( testDB, {aggregate:'foo',
|
|
pipeline: [ {$project : {j : 1}},
|
|
{$group : {_id : 'j', sum : {$sum : '$j'}}}]} );
|
|
}
|
|
}
|
|
|
|
var checkWriteOps = function( hasWriteAuth ) {
|
|
if ( hasWriteAuth ) {
|
|
print( "Checking write operations, should work" );
|
|
testDB.foo.insert({a : 1, i : 1, j : 1});
|
|
res = checkCommandSucceeded( testDB, { findAndModify: "foo", query: {a:1, i:1, j:1},
|
|
update: {$set: {b:1}}});
|
|
assert.eq(1, res.value.a);
|
|
assert.eq(null, res.value.b);
|
|
assert.eq(1, testDB.foo.findOne({a:1}).b);
|
|
testDB.foo.remove({a : 1});
|
|
assert.eq( null, testDB.runCommand({getlasterror : 1}).err );
|
|
checkCommandSucceeded( testDB, {reIndex:'foo'} );
|
|
checkCommandSucceeded( testDB, {repairDatabase : 1} );
|
|
checkCommandSucceeded( testDB, {mapreduce : 'foo', map : map, reduce : reduce,
|
|
out : 'mrOutput'} );
|
|
assert.eq( 100, testDB.mrOutput.count() );
|
|
assert.eq( 45, testDB.mrOutput.findOne().value );
|
|
|
|
checkCommandSucceeded( testDB, {drop : 'foo'} );
|
|
assert.eq( 0, testDB.foo.count() );
|
|
testDB.foo.insert({a:1});
|
|
assert.eq( 1, testDB.foo.count() );
|
|
checkCommandSucceeded( testDB, {dropDatabase : 1} );
|
|
assert.eq( 0, testDB.foo.count() );
|
|
checkCommandSucceeded( testDB, {create : 'baz'} );
|
|
} else {
|
|
print( "Checking write operations, should fail" );
|
|
testDB.foo.insert({a : 1, i : 1, j : 1});
|
|
assert.eq(0, authenticatedConn.getDB('test').foo.count({a : 1, i : 1, j : 1}));
|
|
checkCommandFailed( testDB, { findAndModify: "foo", query: {a:1, i:1, j:1},
|
|
update: {$set: {b:1}}} );
|
|
checkCommandFailed( testDB, {reIndex:'foo'} );
|
|
checkCommandFailed( testDB, {repairDatabase : 1} );
|
|
checkCommandFailed( testDB, {mapreduce : 'foo', map : map, reduce : reduce,
|
|
out : 'mrOutput'} );
|
|
checkCommandFailed( testDB, {drop : 'foo'} );
|
|
checkCommandFailed( testDB, {dropDatabase : 1} );
|
|
passed = true;
|
|
try {
|
|
// For some reason when create fails it throws an exception instead of just returning ok:0
|
|
res = testDB.runCommand( {create : 'baz'} );
|
|
if ( !res.ok ) {
|
|
passed = false;
|
|
}
|
|
} catch (e) {
|
|
// expected
|
|
printjson(e);
|
|
passed = false;
|
|
}
|
|
assert( !passed );
|
|
}
|
|
}
|
|
|
|
var checkAdminOps = function( hasAuth ) {
|
|
if ( hasAuth ) {
|
|
checkCommandSucceeded( adminDB, {getCmdLineOpts : 1} );
|
|
checkCommandSucceeded( adminDB, {serverStatus : 1} );
|
|
checkCommandSucceeded( adminDB, {listShards : 1} );
|
|
checkCommandSucceeded( adminDB, {whatsmyuri : 1} );
|
|
checkCommandSucceeded( adminDB, {isdbgrid : 1} );
|
|
checkCommandSucceeded( adminDB, {ismaster : 1} );
|
|
checkCommandSucceeded( adminDB, {split : 'test.foo', find : {i : 1, j : 1}} );
|
|
chunk = configDB.chunks.findOne({ shard : st.rs0.name });
|
|
checkCommandSucceeded( adminDB, {moveChunk : 'test.foo', find : chunk.min,
|
|
to : st.rs1.name, _waitForDelete : true} );
|
|
} else {
|
|
checkCommandFailed( adminDB, {getCmdLineOpts : 1} );
|
|
checkCommandFailed( adminDB, {serverStatus : 1} );
|
|
checkCommandFailed( adminDB, {listShards : 1} );
|
|
// whatsmyuri, isdbgrid, and ismaster don't require any auth
|
|
checkCommandSucceeded( adminDB, {whatsmyuri : 1} );
|
|
checkCommandSucceeded( adminDB, {isdbgrid : 1} );
|
|
checkCommandSucceeded( adminDB, {ismaster : 1} );
|
|
checkCommandFailed( adminDB, {split : 'test.foo', find : {i : 1, j : 1}} );
|
|
chunkKey = { i : { $minKey : 1 }, j : { $minKey : 1 } };
|
|
checkCommandFailed( adminDB, {moveChunk : 'test.foo', find : chunkKey,
|
|
to : st.rs1.name, _waitForDelete : true} );
|
|
|
|
}
|
|
}
|
|
|
|
var checkRemoveShard = function( hasWriteAuth ) {
|
|
if ( hasWriteAuth ) {
|
|
// start draining
|
|
checkCommandSucceeded( adminDB, { removeshard : st.rs1.name } );
|
|
// Wait for shard to be completely removed
|
|
checkRemoveShard = function() {
|
|
res = checkCommandSucceeded( adminDB, { removeshard : st.rs1.name } );
|
|
return res.msg == 'removeshard completed successfully';
|
|
}
|
|
assert.soon( checkRemoveShard , "failed to remove shard" );
|
|
} else {
|
|
checkCommandFailed( adminDB, { removeshard : st.rs1.name } );
|
|
}
|
|
}
|
|
|
|
var checkAddShard = function( hasWriteAuth ) {
|
|
if ( hasWriteAuth ) {
|
|
checkCommandSucceeded( adminDB, { addshard : st.rs1.getURL() } );
|
|
} else {
|
|
checkCommandFailed( adminDB, { addshard : st.rs1.getURL() } );
|
|
}
|
|
}
|
|
|
|
|
|
st.stopBalancer();
|
|
|
|
jsTestLog("Checking admin commands with admin auth credentials");
|
|
checkAdminOps( true );
|
|
assert( adminDB.logout().ok );
|
|
|
|
jsTestLog("Checking admin commands with no auth credentials");
|
|
checkAdminOps( false );
|
|
|
|
jsTestLog("Checking commands with no auth credentials");
|
|
checkReadOps( false );
|
|
checkWriteOps( false );
|
|
|
|
// Authenticate as read-only user
|
|
jsTestLog("Checking commands with read-only auth credentials");
|
|
assert( testDB.auth( roUser, password ) );
|
|
checkReadOps( true );
|
|
checkWriteOps( false );
|
|
|
|
// Authenticate as read-write user
|
|
jsTestLog("Checking commands with read-write auth credentials");
|
|
assert( testDB.auth( rwUser, password ) );
|
|
checkReadOps( true );
|
|
checkWriteOps( true );
|
|
|
|
|
|
jsTestLog("Check drainging/removing a shard");
|
|
assert( testDB.logout().ok );
|
|
checkRemoveShard( false );
|
|
assert( adminDB.auth( rwUser, password ) );
|
|
assert( testDB.dropDatabase().ok );
|
|
checkRemoveShard( true );
|
|
adminDB.printShardingStatus();
|
|
|
|
jsTestLog("Check adding a shard")
|
|
assert( adminDB.logout().ok );
|
|
checkAddShard( false );
|
|
assert( adminDB.auth( rwUser, password ) );
|
|
checkAddShard( true );
|
|
adminDB.printShardingStatus();
|
|
|
|
|
|
st.stop();
|
|
}
|
|
|
|
doTest();
|