mongo/jstests/replsets/check_replication_hello_res...

377 lines
12 KiB
JavaScript

/**
* Checks all the easily testable fields in the response object returned by the hello() command and
* its aliases, isMaster() and ismaster(). This test also checks that fields that should not be in
* the document are absent.
* @tags: [
* ]
*/
// Skip db hash check because node 2 is slave delayed and may time out on awaitReplication.
TestData.skipCheckDBHashes = true;
load("jstests/replsets/rslib.js");
// function create the error message if an assert fails
var generateErrorString = function(badFields, missingFields, badValues, result) {
var str = "\nThe result was:\n" + tojson(result);
if (badFields.length !== 0) {
str += "\nIt had the following fields which it shouldn't have: ";
str += badFields;
}
if (missingFields.length !== 0) {
str += "\nIt lacked the following fields which it should have contained: ";
str += missingFields;
}
if (badValues.length !== 0) {
for (i = 0; i < badValues.length; i += 3) {
str += "\nIts value for " + badValues[i] + " is " + badValues[i + 1];
str += " but should be " + badValues[i + 2];
}
}
return str;
};
// This function calls checkResponseFields with the isMaster and hello commands.
var runHelloCmdAndAliases = function(memberInfo) {
checkResponseFields(memberInfo, "ismaster");
checkResponseFields(memberInfo, "isMaster");
checkResponseFields(memberInfo, "hello");
};
// This function runs either the isMaster or hello command, and validates that the response is what
// we expect.
var checkResponseFields = function(memberInfo, cmd) {
// run the passed in command on the connection
var result = memberInfo.conn.getDB("admin").runCommand(cmd);
// If we are running the hello command, we must modify the expected fields. We expect
// "isWritablePrimary" and "secondaryDelaySecs" instead of "ismaster" and "slaveDelay" in the
// hello command response.
if (cmd === "hello") {
memberInfo.goodValues.isWritablePrimary = memberInfo.goodValues.ismaster;
delete memberInfo.goodValues.ismaster;
memberInfo.unwantedFields.push("ismaster");
memberInfo.unwantedFields =
memberInfo.unwantedFields.filter(f => f !== "isWritablePrimary");
if (memberInfo.goodValues.hasOwnProperty("slaveDelay")) {
memberInfo.goodValues.secondaryDelaySecs = memberInfo.goodValues.slaveDelay;
delete memberInfo.goodValues.slaveDelay;
memberInfo.unwantedFields.push("slaveDelay");
memberInfo.unwantedFields =
memberInfo.unwantedFields.filter(f => f !== "secondaryDelaySecs");
}
}
// make sure result doesn't contain anything it shouldn't
var badFields = [];
for (field in result) {
if (!result.hasOwnProperty(field)) {
continue;
}
if (Array.contains(memberInfo.unwantedFields, field)) {
badFields.push(field);
}
}
// make sure result contains the fields we want
var missingFields = [];
for (i = 0; i < memberInfo.wantedFields.length; i++) {
field = memberInfo.wantedFields[i];
if (!result.hasOwnProperty(field)) {
missingFields.push(field);
print(field);
}
}
// make sure the result has proper values for fields with known values
var badValues = []; // each mistake will be saved as three entries (key, badvalue, goodvalue)
for (field in memberInfo.goodValues) {
if (typeof (memberInfo.goodValues[field]) === "object") {
// assumes nested obj is disk in tags this is currently true, but may change
if (result[field].disk !== memberInfo.goodValues[field].disk) {
badValues.push("tags.disk");
badValues.push(result[field].disk);
badValues.push(memberInfo.goodValues[field].disk);
}
} else {
if (result[field] !== memberInfo.goodValues[field]) {
badValues.push(field);
badValues.push(result[field]);
badValues.push(memberInfo.goodValues[field]);
}
}
}
assert(badFields.length === 0 && missingFields.length === 0 && badValues.length === 0,
memberInfo.name + " had the following problems." +
generateErrorString(badFields, missingFields, badValues, result));
};
// start of test code
var name = "hello_and_aliases";
var replTest = new ReplSetTest({name: name, nodes: 4});
var nodes = replTest.startSet();
var config = replTest.getReplSetConfig();
config.members[1].priority = 0;
config.members[2].priority = 0;
config.members[2].hidden = true;
config.members[2].secondaryDelaySecs = 3;
config.members[2].buildIndexes = false;
config.members[3].arbiterOnly = true;
replTest.initiate(config);
var agreeOnPrimaryAndSetVersion = function(setVersion) {
print("Waiting for primary and replica set version " + setVersion);
var nodes = replTest.nodes;
var currPrimary = undefined;
var lastSetVersion = setVersion;
for (var i = 0; i < nodes.length; i++) {
try {
var helloResult = nodes[i].getDB("admin").runCommand({hello: 1});
} catch (e) {
// handle reconnect errors due to step downs
print("Error while calling hello on " + nodes[i] + ": " + e);
return false;
}
printjson(helloResult);
if (!currPrimary)
currPrimary = helloResult.primary;
if (!lastSetVersion)
lastSetVersion = helloResult.setVersion;
if (helloResult.primary != currPrimary || !currPrimary)
return false;
if (helloResult.setVersion != lastSetVersion)
return false;
}
return true;
};
var primary = replTest.getPrimary();
var secondaries = replTest.getSecondaries();
var expectedVersion = replTest.getReplSetConfigFromNode().version;
assert.soon(function() {
return agreeOnPrimaryAndSetVersion(expectedVersion);
}, "Nodes did not initiate in less than a minute", 60000);
// Check to see if the information from hello() and its aliases are correct at each node.
// The checker only checks that the field exists when its value is "has".
runHelloCmdAndAliases({
conn: primary,
name: "primary",
goodValues: {
setName: "hello_and_aliases",
setVersion: expectedVersion,
ismaster: true,
secondary: false,
ok: 1
},
wantedFields:
["hosts", "passives", "arbiters", "primary", "me", "maxBsonObjectSize", "localTime"],
unwantedFields: [
"isWritablePrimary",
"arbiterOnly",
"passive",
"slaveDelay",
"secondaryDelaySecs",
"hidden",
"tags",
"buildIndexes"
]
});
runHelloCmdAndAliases({
conn: secondaries[0],
name: "secondary",
goodValues: {
setName: "hello_and_aliases",
setVersion: expectedVersion,
ismaster: false,
secondary: true,
passive: true,
ok: 1
},
wantedFields:
["hosts", "passives", "arbiters", "primary", "me", "maxBsonObjectSize", "localTime"],
unwantedFields: [
"isWritablePrimary",
"arbiterOnly",
"slaveDelay",
"secondaryDelaySecs",
"hidden",
"tags",
"buildIndexes"
]
});
runHelloCmdAndAliases({
conn: secondaries[1],
name: "delayed_secondary",
goodValues: {
setName: "hello_and_aliases",
setVersion: expectedVersion,
ismaster: false,
secondary: true,
passive: true,
slaveDelay: 3,
buildIndexes: false,
ok: 1
},
wantedFields:
["hosts", "passives", "arbiters", "primary", "me", "maxBsonObjectSize", "localTime"],
unwantedFields: ["isWritablePrimary", "arbiterOnly", "tags", "secondaryDelaySecs"]
});
runHelloCmdAndAliases({
conn: secondaries[2],
name: "arbiter",
goodValues: {
setName: "hello_and_aliases",
setVersion: expectedVersion,
ismaster: false,
secondary: false,
arbiterOnly: true,
ok: 1
},
wantedFields:
["hosts", "passives", "arbiters", "primary", "me", "maxBsonObjectSize", "localTime"],
unwantedFields: [
"isWritablePrimary",
"slaveDelay",
"secondaryDelaySecs",
"hidden",
"tags",
"buildIndexes",
"passive"
]
});
// Reconfigure the replset and make sure the changes are present on all members.
config = primary.getDB("local").system.replset.findOne();
config.version = config.version + 1;
config.members[0].tags = {
disk: "ssd"
};
config.members[1].tags = {
disk: "ssd"
};
config.members[1].hidden = true;
config.members[2].secondaryDelaySecs = 300000;
config.members[2].tags = {
disk: "hdd"
};
try {
result = primary.getDB("admin").runCommand({replSetReconfig: config});
} catch (e) {
print(e);
}
primary = replTest.getPrimary();
secondaries = replTest.getSecondaries();
expectedVersion = config.version;
assert.soon(function() {
return agreeOnPrimaryAndSetVersion(expectedVersion);
}, "Nodes did not sync in less than a minute", 60000);
// check nodes for their new settings
runHelloCmdAndAliases({
conn: primary,
name: "primary2",
goodValues: {
setName: "hello_and_aliases",
setVersion: expectedVersion,
ismaster: true,
secondary: false,
tags: {"disk": "ssd"},
ok: 1
},
wantedFields: ["hosts", "arbiters", "primary", "me", "maxBsonObjectSize", "localTime"],
unwantedFields: [
"isWritablePrimary",
"arbiterOnly",
"passives",
"passive",
"slaveDelay",
"secondaryDelaySecs",
"hidden",
"buildIndexes"
]
});
runHelloCmdAndAliases({
conn: secondaries[0],
name: "first_secondary",
goodValues: {
setName: "hello_and_aliases",
setVersion: expectedVersion,
ismaster: false,
secondary: true,
tags: {"disk": "ssd"},
passive: true,
hidden: true,
ok: 1
},
wantedFields: ["hosts", "arbiters", "primary", "me", "maxBsonObjectSize", "localTime"],
unwantedFields: [
"isWritablePrimary",
"arbiterOnly",
"passives",
"slaveDelay",
"secondaryDelaySecs",
"buildIndexes"
]
});
runHelloCmdAndAliases({
conn: secondaries[1],
name: "very_delayed_secondary",
goodValues: {
setName: "hello_and_aliases",
setVersion: expectedVersion,
ismaster: false,
secondary: true,
tags: {"disk": "hdd"},
passive: true,
slaveDelay: 300000,
buildIndexes: false,
hidden: true,
ok: 1
},
wantedFields: ["hosts", "arbiters", "primary", "me", "maxBsonObjectSize", "localTime"],
unwantedFields: ["isWritablePrimary", "arbiterOnly", "passives", "secondaryDelaySecs"]
});
runHelloCmdAndAliases({
conn: secondaries[2],
name: "arbiter",
goodValues: {
setName: "hello_and_aliases",
setVersion: expectedVersion,
ismaster: false,
secondary: false,
arbiterOnly: true,
ok: 1
},
wantedFields: ["hosts", "arbiters", "primary", "me", "maxBsonObjectSize", "localTime"],
unwantedFields: [
"isWritablePrimary",
"slaveDelay",
"secondaryDelaySecs",
"hidden",
"tags",
"buildIndexes",
"passive"
]
});
// force reconfig and ensure all have the same setVersion afterwards
config = primary.getDB("local").system.replset.findOne();
primary.getDB("admin").runCommand({replSetReconfig: config, force: true});
assert.soon(function() {
return agreeOnPrimaryAndSetVersion();
}, "Nodes did not sync in less than a minute after forced reconfig", 60000);
replTest.stopSet();