// Check that the output from printShardingStatus() (aka sh.status()) // contains important information that it should, like the major section // headings and the names of sharded collections and their shard keys. (function () { var st = new ShardingTest({ shards: 1, mongos: 2, config: 1, other: { smallfiles: true } }); var mongos = st.s0; var admin = mongos.getDB( "admin" ); function grabStatusOutput(configdb, verbose) { var res = print.captureAllOutput( function () { return printShardingStatus(configdb, verbose); } ); var output = res.output.join("\n"); jsTestLog(output); return output; } function assertPresentInOutput(output, content, what) { assert(output.includes(content), what + " \"" + content + "\" NOT present in output of " + "printShardingStatus() (but it should be)"); } function assertNotPresentInOutput(output, content, what) { assert( ! output.includes(content), what + " \"" + content + "\" IS present in output of " + "printShardingStatus() (but it should not be)"); } //////////////////////// // Basic tests //////////////////////// var dbName = "thisIsTheDatabase"; var collName = "thisIsTheCollection"; var shardKeyName = "thisIsTheShardKey"; var nsName = dbName + "." + collName; assert.commandWorked( admin.runCommand({ enableSharding: dbName }) ); var key = {}; key[shardKeyName] = 1; assert.commandWorked( admin.runCommand({ shardCollection: nsName, key: key }) ); function testBasic(output) { assertPresentInOutput(output, "shards:", "section header"); assertPresentInOutput(output, "databases:", "section header"); assertPresentInOutput(output, "balancer:", "section header"); assertPresentInOutput(output, "active mongoses:", "section header"); assertNotPresentInOutput(output, "most recently active mongoses:", "section header"); assertPresentInOutput(output, dbName, "database"); assertPresentInOutput(output, collName, "collection"); assertPresentInOutput(output, shardKeyName, "shard key"); } function testBasicNormalOnly(output) { assertPresentInOutput(output, tojson(version) + " : 2\n", "active mongos version"); } function testBasicVerboseOnly(output) { assertPresentInOutput(output, '"mongoVersion" : ' + tojson(version), "active mongos version"); assertPresentInOutput(output, '"_id" : ' + tojson(s1Host), "active mongos hostname"); assertPresentInOutput(output, '"_id" : ' + tojson(s2Host), "active mongos hostname"); } var buildinfo = assert.commandWorked( mongos.adminCommand("buildinfo") ); var serverStatus1 = assert.commandWorked( mongos.adminCommand("serverStatus") ); var serverStatus2 = assert.commandWorked( st.s1.adminCommand("serverStatus") ); var version = buildinfo.version; var s1Host = serverStatus1.host; var s2Host = serverStatus2.host; // Normal, active mongoses var outputNormal = grabStatusOutput(st.config, false); testBasic(outputNormal); testBasicNormalOnly(outputNormal); var outputVerbose = grabStatusOutput(st.config, true); testBasic(outputVerbose); testBasicVerboseOnly(outputVerbose); // Take a copy of the config db, in order to test the harder-to-setup cases below. // TODO: Replace this manual copy with copydb once SERVER-13080 is fixed. var config = mongos.getDB("config"); var configCopy = mongos.getDB("configCopy"); config.getCollectionInfos().forEach( function (c) { // Create collection with options. assert.commandWorked( configCopy.createCollection(c.name, c.options) ); // Clone the docs. config.getCollection(c.name).find().snapshot().forEach( function (d) { assert.writeOK( configCopy.getCollection(c.name).insert(d) ); } ); // Build the indexes. config.getCollection(c.name).getIndexes().forEach( function (i) { var key = i.key; delete i.key; delete i.ns; delete i.v; assert.commandWorked( configCopy.getCollection(c.name).ensureIndex(key, i) ); } ); } ); // Inactive mongoses // Make the first ping be older than now by 1 second more than the threshold // Make the second ping be older still by the same amount again var pingAdjustMs = 60000 + 1000; var then = new Date(); then.setTime(then.getTime() - pingAdjustMs); configCopy.mongos.update( { _id: s1Host }, { $set: { ping: then } } ); then.setTime(then.getTime() - pingAdjustMs); configCopy.mongos.update( { _id: s2Host }, { $set: { ping: then } } ); var output = grabStatusOutput(configCopy, false); assertPresentInOutput(output, "most recently active mongoses:", "section header"); assertPresentInOutput(output, tojson(version) + " : 1\n", "recent mongos version"); var output = grabStatusOutput(configCopy, true); assertPresentInOutput(output, "most recently active mongoses:", "section header"); assertPresentInOutput(output, '"_id" : ' + tojson(s1Host), "recent mongos hostname"); assertNotPresentInOutput(output, '"_id" : ' + tojson(s2Host), "old mongos hostname"); // Older mongoses configCopy.mongos.remove( { _id: s1Host } ); var output = grabStatusOutput(configCopy, false); assertPresentInOutput(output, "most recently active mongoses:", "section header"); assertPresentInOutput(output, tojson(version) + " : 1\n", "recent mongos version"); var output = grabStatusOutput(configCopy, true); assertPresentInOutput(output, "most recently active mongoses:", "section header"); assertNotPresentInOutput(output, '"_id" : ' + tojson(s1Host), "removed mongos hostname"); assertPresentInOutput(output, '"_id" : ' + tojson(s2Host), "recent mongos hostname"); // No mongoses at all configCopy.mongos.remove({}); var output = grabStatusOutput(configCopy, false); assertPresentInOutput(output, "most recently active mongoses:\n\tnone", "no mongoses"); var output = grabStatusOutput(configCopy, true); assertPresentInOutput(output, "most recently active mongoses:\n\tnone", "no mongoses (verbose)"); assert( mongos.getDB(dbName).dropDatabase() ); //////////////////////// // Extended tests //////////////////////// var testCollDetailsNum = 0; function testCollDetails(args) { if (args === undefined || typeof(args) != "object") { args = {}; } var getCollName = function (x) { return "test.test" + x.zeroPad(4); }; var collName = getCollName(testCollDetailsNum); var cmdObj = { shardCollection: collName, key: { _id: 1 } }; if (args.unique) { cmdObj.unique = true; } assert.commandWorked( admin.runCommand(cmdObj) ); if (args.hasOwnProperty("unique")) { assert.writeOK( mongos.getDB("config").collections.update({ _id : collName }, { $set : { "unique" : args.unique } }) ); } if (args.hasOwnProperty("noBalance")) { assert.writeOK( mongos.getDB("config").collections.update({ _id : collName }, { $set : { "noBalance" : args.noBalance } }) ); } var output = grabStatusOutput(st.config); assertPresentInOutput(output, collName, "collection"); // If any of the previous collection names are present, then their optional indicators // might also be present. This might taint the results when we go searching through // the output. // This also means that earlier collNames can't be a prefix of later collNames. for (var i = 0; i < testCollDetailsNum; i++) { assertNotPresentInOutput(output, getCollName(i), "previous collection"); } assertPresentInOutput(output, "unique: " + (!!args.unique), "unique shard key indicator"); if (args.hasOwnProperty("unique") && typeof(args.unique) != "boolean") { // non-bool: actual value must be shown assertPresentInOutput(output, tojson(args.unique), "unique shard key indicator (non bool)"); } assertPresentInOutput(output, "balancing: " + (!args.noBalance), "balancing indicator (inverse of noBalance)"); if (args.hasOwnProperty("noBalance") && typeof(args.noBalance) != "boolean") { // non-bool: actual value must be shown assertPresentInOutput(output, tojson(args.noBalance), "noBalance indicator (non bool)"); } assert( mongos.getCollection(collName).drop() ); testCollDetailsNum++; } assert.commandWorked( admin.runCommand({ enableSharding: "test" }) ); // Defaults testCollDetails({ }); // Expected values testCollDetails({ unique: false, noBalance: false }); testCollDetails({ unique: true, noBalance: true }); // Unexpected truthy values testCollDetails({ unique: "truthy unique value 1", noBalance: "truthy noBalance value 1" }); testCollDetails({ unique: 1, noBalance: 1 }); testCollDetails({ unique: -1, noBalance: -1 }); testCollDetails({ unique: {}, noBalance: {} }); // Unexpected falsy values testCollDetails({ unique: "", noBalance: "" }); testCollDetails({ unique: 0, noBalance: 0 }); assert( mongos.getDB("test").dropDatabase() ); st.stop(); })();