/**
* Copyright 2014 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/bson/json.h"
#include "mongo/bson/mutable/document.h"
#include "mongo/bson/mutable/element.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/repl/replica_set_config.h"
#include "mongo/db/server_options.h"
#include "mongo/util/scopeguard.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
namespace repl {
namespace {
// Creates a bson document reprsenting a replica set config doc with the given members, and votes
BSONObj createConfigDoc(int members, int voters = ReplicaSetConfig::kMaxVotingMembers) {
str::stream configJson;
configJson << "{_id:'rs0', version:1, members:[";
for (int i = 0; i < members; ++i) {
configJson << "{_id:" << i << ", host:'node" << i << "'";
if (i >= voters) {
configJson << ", votes:0, priority:0";
}
configJson << "}";
if (i != (members - 1))
configJson << ",";
}
configJson << "]}";
return fromjson(configJson);
}
TEST(ReplicaSetConfig, ParseMinimalConfigAndCheckDefaults) {
ReplicaSetConfig config;
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
ASSERT_OK(config.validate());
ASSERT_EQUALS("rs0", config.getReplSetName());
ASSERT_EQUALS(1, config.getConfigVersion());
ASSERT_EQUALS(1, config.getNumMembers());
ASSERT_EQUALS(0, config.membersBegin()->getId());
ASSERT_EQUALS(1, config.getDefaultWriteConcern().wNumNodes);
ASSERT_EQUALS("", config.getDefaultWriteConcern().wMode);
ASSERT_EQUALS(ReplicaSetConfig::kDefaultHeartbeatInterval, config.getHeartbeatInterval());
ASSERT_EQUALS(ReplicaSetConfig::kDefaultHeartbeatTimeoutPeriod,
config.getHeartbeatTimeoutPeriod());
ASSERT_EQUALS(ReplicaSetConfig::kDefaultElectionTimeoutPeriod,
config.getElectionTimeoutPeriod());
ASSERT_TRUE(config.isChainingAllowed());
ASSERT_FALSE(config.getWriteConcernMajorityShouldJournal());
ASSERT_FALSE(config.isConfigServer());
ASSERT_EQUALS(0, config.getProtocolVersion());
}
TEST(ReplicaSetConfig, ParseLargeConfigAndCheckAccessors) {
ReplicaSetConfig config;
ASSERT_OK(config.initialize(BSON(
"_id"
<< "rs0"
<< "version" << 1234 << "members" << BSON_ARRAY(BSON("_id" << 234 << "host"
<< "localhost:12345"
<< "tags" << BSON("NYC"
<< "NY")))
<< "protocolVersion" << 1 << "settings"
<< BSON("getLastErrorDefaults" << BSON("w"
<< "majority") << "getLastErrorModes"
<< BSON("eastCoast" << BSON("NYC" << 1)) << "chainingAllowed"
<< false << "heartbeatIntervalMillis" << 5000
<< "heartbeatTimeoutSecs" << 120 << "electionTimeoutMillis"
<< 10))));
ASSERT_OK(config.validate());
ASSERT_EQUALS("rs0", config.getReplSetName());
ASSERT_EQUALS(1234, config.getConfigVersion());
ASSERT_EQUALS(1, config.getNumMembers());
ASSERT_EQUALS(234, config.membersBegin()->getId());
ASSERT_EQUALS(0, config.getDefaultWriteConcern().wNumNodes);
ASSERT_EQUALS("majority", config.getDefaultWriteConcern().wMode);
ASSERT_FALSE(config.isChainingAllowed());
ASSERT_TRUE(config.getWriteConcernMajorityShouldJournal());
ASSERT_FALSE(config.isConfigServer());
ASSERT_EQUALS(Seconds(5), config.getHeartbeatInterval());
ASSERT_EQUALS(Seconds(120), config.getHeartbeatTimeoutPeriod());
ASSERT_EQUALS(Milliseconds(10), config.getElectionTimeoutPeriod());
ASSERT_EQUALS(1, config.getProtocolVersion());
}
TEST(ReplicaSetConfig, MajorityCalculationThreeVotersNoArbiters) {
ReplicaSetConfig config;
ASSERT_OK(
config.initialize(BSON("_id"
<< "rs0"
<< "version" << 2 << "members"
<< BSON_ARRAY(BSON("_id" << 1 << "host"
<< "h1:1")
<< BSON("_id" << 2 << "host"
<< "h2:1") << BSON("_id" << 3 << "host"
<< "h3:1")
<< BSON("_id" << 4 << "host"
<< "h4:1"
<< "votes" << 0 << "priority" << 0)
<< BSON("_id" << 5 << "host"
<< "h5:1"
<< "votes" << 0 << "priority" << 0)))));
ASSERT_OK(config.validate());
ASSERT_EQUALS(2, config.getWriteMajority());
}
TEST(ReplicaSetConfig, MajorityCalculationNearlyHalfArbiters) {
ReplicaSetConfig config;
ASSERT_OK(
config.initialize(BSON("_id"
<< "mySet"
<< "version" << 2 << "members"
<< BSON_ARRAY(BSON("host"
<< "node1:12345"
<< "_id" << 0)
<< BSON("host"
<< "node2:12345"
<< "_id" << 1) << BSON("host"
<< "node3:12345"
<< "_id" << 2)
<< BSON("host"
<< "node4:12345"
<< "_id" << 3 << "arbiterOnly" << true)
<< BSON("host"
<< "node5:12345"
<< "_id" << 4 << "arbiterOnly" << true)))));
ASSERT_OK(config.validate());
ASSERT_EQUALS(3, config.getWriteMajority());
}
TEST(ReplicaSetConfig, MajorityCalculationEvenNumberOfMembers) {
ReplicaSetConfig config;
ASSERT_OK(config.initialize(BSON("_id"
<< "mySet"
<< "version" << 2 << "members"
<< BSON_ARRAY(BSON("host"
<< "node1:12345"
<< "_id" << 0)
<< BSON("host"
<< "node2:12345"
<< "_id" << 1) << BSON("host"
<< "node3:12345"
<< "_id" << 2)
<< BSON("host"
<< "node4:12345"
<< "_id" << 3)))));
ASSERT_OK(config.validate());
ASSERT_EQUALS(3, config.getWriteMajority());
}
TEST(ReplicaSetConfig, MajorityCalculationNearlyHalfSecondariesNoVotes) {
ReplicaSetConfig config;
ASSERT_OK(
config.initialize(BSON("_id"
<< "mySet"
<< "version" << 2 << "members"
<< BSON_ARRAY(
BSON("host"
<< "node1:12345"
<< "_id" << 0)
<< BSON("host"
<< "node2:12345"
<< "_id" << 1 << "votes" << 0 << "priority" << 0)
<< BSON("host"
<< "node3:12345"
<< "_id" << 2 << "votes" << 0 << "priority" << 0)
<< BSON("host"
<< "node4:12345"
<< "_id" << 3) << BSON("host"
<< "node5:12345"
<< "_id" << 4)))));
ASSERT_OK(config.validate());
ASSERT_EQUALS(2, config.getWriteMajority());
}
TEST(ReplicaSetConfig, ParseFailsWithBadOrMissingIdField) {
ReplicaSetConfig config;
// Replica set name must be a string.
ASSERT_EQUALS(ErrorCodes::TypeMismatch,
config.initialize(BSON("_id" << 1 << "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
// Replica set name must be present.
ASSERT_EQUALS(
ErrorCodes::NoSuchKey,
config.initialize(
BSON("version" << 1 << "members" << BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
// Empty repl set name parses, but does not validate.
ASSERT_OK(config.initialize(BSON("_id"
<< ""
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
ASSERT_EQUALS(ErrorCodes::BadValue, config.validate());
}
TEST(ReplicaSetConfig, ParseFailsWithBadOrMissingVersionField) {
ReplicaSetConfig config;
// Config version field must be present.
ASSERT_EQUALS(
ErrorCodes::NoSuchKey,
config.initialize(BSON("_id"
<< "rs0"
<< "members" << BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
ASSERT_EQUALS(
ErrorCodes::TypeMismatch,
config.initialize(BSON("_id"
<< "rs0"
<< "version"
<< "1"
<< "members" << BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1.0 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
ASSERT_OK(config.validate());
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 0.0 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
ASSERT_EQUALS(ErrorCodes::BadValue, config.validate());
ASSERT_OK(
config.initialize(BSON("_id"
<< "rs0"
<< "version"
<< static_cast(std::numeric_limits::max()) + 1
<< "members" << BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
ASSERT_EQUALS(ErrorCodes::BadValue, config.validate());
}
TEST(ReplicaSetConfig, ParseFailsWithBadMembers) {
ReplicaSetConfig config;
ASSERT_EQUALS(ErrorCodes::TypeMismatch,
config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")
<< "localhost:23456"))));
ASSERT_NOT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("host"
<< "localhost:12345")))));
}
TEST(ReplicaSetConfig, ParseFailsWithLocalNonLocalHostMix) {
ReplicaSetConfig config;
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost")
<< BSON("_id" << 1 << "host"
<< "otherhost")))));
ASSERT_EQUALS(ErrorCodes::BadValue, config.validate());
}
TEST(ReplicaSetConfig, ParseFailsWithNoElectableNodes) {
ReplicaSetConfig config;
const BSONObj configBsonNoElectableNodes = BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:1"
<< "priority" << 0)
<< BSON("_id" << 1 << "host"
<< "localhost:2"
<< "priority"
<< 0)));
ASSERT_OK(config.initialize(configBsonNoElectableNodes));
ASSERT_EQUALS(ErrorCodes::BadValue, config.validate());
const BSONObj configBsonNoElectableNodesOneArbiter =
BSON("_id"
<< "rs0"
<< "version" << 1 << "members" << BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:1"
<< "arbiterOnly" << 1)
<< BSON("_id" << 1 << "host"
<< "localhost:2"
<< "priority" << 0)));
ASSERT_OK(config.initialize(configBsonNoElectableNodesOneArbiter));
ASSERT_EQUALS(ErrorCodes::BadValue, config.validate());
const BSONObj configBsonNoElectableNodesTwoArbiters =
BSON("_id"
<< "rs0"
<< "version" << 1 << "members" << BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:1"
<< "arbiterOnly" << 1)
<< BSON("_id" << 1 << "host"
<< "localhost:2"
<< "arbiterOnly" << 1)));
ASSERT_OK(config.initialize(configBsonNoElectableNodesOneArbiter));
ASSERT_EQUALS(ErrorCodes::BadValue, config.validate());
const BSONObj configBsonOneElectableNode = BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:1"
<< "priority" << 0)
<< BSON("_id" << 1 << "host"
<< "localhost:2"
<< "priority"
<< 1)));
ASSERT_OK(config.initialize(configBsonOneElectableNode));
ASSERT_OK(config.validate());
}
TEST(ReplicaSetConfig, ParseFailsWithTooFewVoters) {
ReplicaSetConfig config;
const BSONObj configBsonNoVoters =
BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:1"
<< "votes" << 0 << "priority" << 0)
<< BSON("_id" << 1 << "host"
<< "localhost:2"
<< "votes" << 0 << "priority" << 0)));
ASSERT_OK(config.initialize(configBsonNoVoters));
ASSERT_EQUALS(ErrorCodes::BadValue, config.validate());
const BSONObj configBsonOneVoter = BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:1"
<< "votes" << 0 << "priority"
<< 0)
<< BSON("_id" << 1 << "host"
<< "localhost:2"
<< "votes" << 1)));
ASSERT_OK(config.initialize(configBsonOneVoter));
ASSERT_OK(config.validate());
}
TEST(ReplicaSetConfig, ParseFailsWithTooManyVoters) {
ReplicaSetConfig config;
ASSERT_OK(config.initialize(createConfigDoc(8, ReplicaSetConfig::kMaxVotingMembers)));
ASSERT_OK(config.validate());
ASSERT_OK(config.initialize(createConfigDoc(8, ReplicaSetConfig::kMaxVotingMembers + 1)));
ASSERT_NOT_OK(config.validate());
}
TEST(ReplicaSetConfig, ParseFailsWithDuplicateHost) {
ReplicaSetConfig config;
const BSONObj configBson = BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:1")
<< BSON("_id" << 1 << "host"
<< "localhost:1")));
ASSERT_OK(config.initialize(configBson));
ASSERT_EQUALS(ErrorCodes::BadValue, config.validate());
}
TEST(ReplicaSetConfig, ParseFailsWithTooManyNodes) {
ReplicaSetConfig config;
namespace mmb = mutablebson;
mmb::Document configDoc;
mmb::Element configDocRoot = configDoc.root();
ASSERT_OK(configDocRoot.appendString("_id", "rs0"));
ASSERT_OK(configDocRoot.appendInt("version", 1));
mmb::Element membersArray = configDoc.makeElementArray("members");
ASSERT_OK(configDocRoot.pushBack(membersArray));
for (size_t i = 0; i < ReplicaSetConfig::kMaxMembers; ++i) {
mmb::Element memberElement = configDoc.makeElementObject("");
ASSERT_OK(membersArray.pushBack(memberElement));
ASSERT_OK(memberElement.appendInt("_id", i));
ASSERT_OK(
memberElement.appendString("host", std::string(str::stream() << "localhost" << i + 1)));
if (i >= ReplicaSetConfig::kMaxVotingMembers) {
ASSERT_OK(memberElement.appendInt("votes", 0));
ASSERT_OK(memberElement.appendInt("priority", 0));
}
}
const BSONObj configBsonMaxNodes = configDoc.getObject();
mmb::Element memberElement = configDoc.makeElementObject("");
ASSERT_OK(membersArray.pushBack(memberElement));
ASSERT_OK(memberElement.appendInt("_id", ReplicaSetConfig::kMaxMembers));
ASSERT_OK(memberElement.appendString(
"host", std::string(str::stream() << "localhost" << ReplicaSetConfig::kMaxMembers + 1)));
ASSERT_OK(memberElement.appendInt("votes", 0));
ASSERT_OK(memberElement.appendInt("priority", 0));
const BSONObj configBsonTooManyNodes = configDoc.getObject();
ASSERT_OK(config.initialize(configBsonMaxNodes));
ASSERT_OK(config.validate());
ASSERT_OK(config.initialize(configBsonTooManyNodes));
ASSERT_EQUALS(ErrorCodes::BadValue, config.validate());
}
TEST(ReplicaSetConfig, ParseFailsWithUnexpectedField) {
ReplicaSetConfig config;
Status status = config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "unexpectedfield"
<< "value"));
ASSERT_EQUALS(ErrorCodes::BadValue, status);
}
TEST(ReplicaSetConfig, ParseFailsWithNonArrayMembersField) {
ReplicaSetConfig config;
Status status = config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< "value"));
ASSERT_EQUALS(ErrorCodes::TypeMismatch, status);
}
TEST(ReplicaSetConfig, ParseFailsWithNonNumericHeartbeatIntervalMillisField) {
ReplicaSetConfig config;
Status status = config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"))
<< "settings" << BSON("heartbeatIntervalMillis"
<< "no")));
ASSERT_EQUALS(ErrorCodes::TypeMismatch, status);
ASSERT_FALSE(config.isInitialized());
// Uninitialized configuration should return default heartbeat interval.
ASSERT_EQUALS(ReplicaSetConfig::kDefaultHeartbeatInterval, config.getHeartbeatInterval());
}
TEST(ReplicaSetConfig, ParseFailsWithNonNumericElectionTimeoutMillisField) {
ReplicaSetConfig config;
Status status = config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"))
<< "settings" << BSON("electionTimeoutMillis"
<< "no")));
ASSERT_EQUALS(ErrorCodes::TypeMismatch, status);
}
TEST(ReplicaSetConfig, ParseFailsWithNonNumericHeartbeatTimeoutSecsField) {
ReplicaSetConfig config;
Status status = config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"))
<< "settings" << BSON("heartbeatTimeoutSecs"
<< "no")));
ASSERT_EQUALS(ErrorCodes::TypeMismatch, status);
}
TEST(ReplicaSetConfig, ParseFailsWithNonBoolChainingAllowedField) {
ReplicaSetConfig config;
Status status = config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"))
<< "settings" << BSON("chainingAllowed"
<< "no")));
ASSERT_EQUALS(ErrorCodes::TypeMismatch, status);
}
TEST(ReplicaSetConfig, ParseFailsWithNonBoolConfigServerField) {
ReplicaSetConfig config;
Status status =
config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")) << "configsvr"
<< "no"));
ASSERT_EQUALS(ErrorCodes::TypeMismatch, status);
}
TEST(ReplicaSetConfig, ParseFailsWithNonObjectSettingsField) {
ReplicaSetConfig config;
Status status =
config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")) << "settings"
<< "none"));
ASSERT_EQUALS(ErrorCodes::TypeMismatch, status);
}
TEST(ReplicaSetConfig, ParseFailsWithGetLastErrorDefaultsFieldUnparseable) {
ReplicaSetConfig config;
Status status =
config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")) << "settings"
<< BSON("getLastErrorDefaults" << BSON("fsync"
<< "seven"))));
ASSERT_EQUALS(ErrorCodes::FailedToParse, status);
}
TEST(ReplicaSetConfig, ParseFailsWithNonObjectGetLastErrorDefaultsField) {
ReplicaSetConfig config;
Status status = config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"))
<< "settings" << BSON("getLastErrorDefaults"
<< "no")));
ASSERT_EQUALS(ErrorCodes::TypeMismatch, status);
}
TEST(ReplicaSetConfig, ParseFailsWithNonObjectGetLastErrorModesField) {
ReplicaSetConfig config;
Status status = config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"))
<< "settings" << BSON("getLastErrorModes"
<< "no")));
ASSERT_EQUALS(ErrorCodes::TypeMismatch, status);
}
TEST(ReplicaSetConfig, ParseFailsWithDuplicateGetLastErrorModesField) {
ReplicaSetConfig config;
Status status =
config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "tags" << BSON("tag"
<< "yes"))) << "settings"
<< BSON("getLastErrorModes"
<< BSON("one" << BSON("tag" << 1) << "one"
<< BSON("tag" << 1)))));
ASSERT_EQUALS(ErrorCodes::DuplicateKey, status);
}
TEST(ReplicaSetConfig, ParseFailsWithNonObjectGetLastErrorModesEntryField) {
ReplicaSetConfig config;
Status status =
config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "tags" << BSON("tag"
<< "yes"))) << "settings"
<< BSON("getLastErrorModes" << BSON("one" << 1))));
ASSERT_EQUALS(ErrorCodes::TypeMismatch, status);
}
TEST(ReplicaSetConfig, ParseFailsWithNonNumericGetLastErrorModesConstraintValue) {
ReplicaSetConfig config;
Status status =
config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "tags" << BSON("tag"
<< "yes"))) << "settings"
<< BSON("getLastErrorModes" << BSON("one" << BSON("tag"
<< "no")))));
ASSERT_EQUALS(ErrorCodes::TypeMismatch, status);
}
TEST(ReplicaSetConfig, ParseFailsWithNegativeGetLastErrorModesConstraintValue) {
ReplicaSetConfig config;
Status status =
config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "tags" << BSON("tag"
<< "yes"))) << "settings"
<< BSON("getLastErrorModes" << BSON("one" << BSON("tag" << -1)))));
ASSERT_EQUALS(ErrorCodes::BadValue, status);
}
TEST(ReplicaSetConfig, ParseFailsWithNonExistentGetLastErrorModesConstraintTag) {
ReplicaSetConfig config;
Status status =
config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "tags" << BSON("tag"
<< "yes"))) << "settings"
<< BSON("getLastErrorModes" << BSON("one" << BSON("tag2" << 1)))));
ASSERT_EQUALS(ErrorCodes::NoSuchKey, status);
}
TEST(ReplicaSetConfig, ValidateFailsWithBadProtocolVersion) {
ReplicaSetConfig config;
Status status = config.initialize(BSON("_id"
<< "rs0"
<< "protocolVersion" << 3 << "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")
<< BSON("_id" << 1 << "host"
<< "localhost:54321"))));
ASSERT_OK(status);
status = config.validate();
ASSERT_EQUALS(ErrorCodes::BadValue, status);
}
TEST(ReplicaSetConfig, ValidateFailsWithDuplicateMemberId) {
ReplicaSetConfig config;
Status status = config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")
<< BSON("_id" << 0 << "host"
<< "someoneelse:12345"))));
ASSERT_OK(status);
status = config.validate();
ASSERT_EQUALS(ErrorCodes::BadValue, status);
}
TEST(ReplicaSetConfig, ValidateFailsWithInvalidMember) {
ReplicaSetConfig config;
Status status = config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "hidden" << true))));
ASSERT_OK(status);
status = config.validate();
ASSERT_EQUALS(ErrorCodes::BadValue, status);
}
TEST(ReplicaSetConfig, ChainingAllowedField) {
ReplicaSetConfig config;
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")) << "settings"
<< BSON("chainingAllowed" << true))));
ASSERT_OK(config.validate());
ASSERT_TRUE(config.isChainingAllowed());
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")) << "settings"
<< BSON("chainingAllowed" << false))));
ASSERT_OK(config.validate());
ASSERT_FALSE(config.isChainingAllowed());
}
TEST(ReplicaSetConfig, ConfigServerField) {
ReplicaSetConfig config;
ASSERT_OK(
config.initialize(BSON("_id"
<< "rs0"
<< "protocolVersion" << 1 << "version" << 1 << "configsvr" << true
<< "members" << BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
ASSERT_TRUE(config.isConfigServer());
ReplicaSetConfig config2;
ASSERT_OK(config2.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "configsvr" << false << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
ASSERT_FALSE(config2.isConfigServer());
// Configs in which configsvr is not the same as the --configsvr flag are invalid.
serverGlobalParams.configsvr = true;
ON_BLOCK_EXIT([&] { serverGlobalParams.configsvr = false; });
ASSERT_OK(config.validate());
ASSERT_EQUALS(ErrorCodes::BadValue, config2.validate());
serverGlobalParams.configsvr = false;
ASSERT_EQUALS(ErrorCodes::BadValue, config.validate());
ASSERT_OK(config2.validate());
}
TEST(ReplicaSetConfig, ConfigServerFieldDefaults) {
serverGlobalParams.configsvr = false;
ReplicaSetConfig config;
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "protocolVersion" << 1 << "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
ASSERT_FALSE(config.isConfigServer());
ReplicaSetConfig config2;
ASSERT_OK(
config2.initializeForInitiate(BSON("_id"
<< "rs0"
<< "protocolVersion" << 1 << "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
ASSERT_FALSE(config2.isConfigServer());
serverGlobalParams.configsvr = true;
ON_BLOCK_EXIT([&] { serverGlobalParams.configsvr = false; });
ReplicaSetConfig config3;
ASSERT_OK(config3.initialize(BSON("_id"
<< "rs0"
<< "protocolVersion" << 1 << "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
ASSERT_FALSE(config3.isConfigServer());
ReplicaSetConfig config4;
ASSERT_OK(
config4.initializeForInitiate(BSON("_id"
<< "rs0"
<< "protocolVersion" << 1 << "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
ASSERT_TRUE(config4.isConfigServer());
}
TEST(ReplicaSetConfig, HeartbeatIntervalField) {
ReplicaSetConfig config;
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")) << "settings"
<< BSON("heartbeatIntervalMillis" << 5000))));
ASSERT_OK(config.validate());
ASSERT_EQUALS(Seconds(5), config.getHeartbeatInterval());
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")) << "settings"
<< BSON("heartbeatIntervalMillis" << -5000))));
ASSERT_EQUALS(ErrorCodes::BadValue, config.validate());
}
TEST(ReplicaSetConfig, ElectionTimeoutField) {
ReplicaSetConfig config;
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")) << "settings"
<< BSON("electionTimeoutMillis" << 20))));
ASSERT_OK(config.validate());
ASSERT_EQUALS(Milliseconds(20), config.getElectionTimeoutPeriod());
auto status = config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"))
<< "settings" << BSON("electionTimeoutMillis" << -20)));
ASSERT_EQUALS(ErrorCodes::BadValue, status);
ASSERT_STRING_CONTAINS(status.reason(), "election timeout must be greater than 0");
}
TEST(ReplicaSetConfig, HeartbeatTimeoutField) {
ReplicaSetConfig config;
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")) << "settings"
<< BSON("heartbeatTimeoutSecs" << 20))));
ASSERT_OK(config.validate());
ASSERT_EQUALS(Seconds(20), config.getHeartbeatTimeoutPeriod());
auto status = config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"))
<< "settings" << BSON("heartbeatTimeoutSecs" << -20)));
ASSERT_EQUALS(ErrorCodes::BadValue, status);
ASSERT_STRING_CONTAINS(status.reason(), "heartbeat timeout must be greater than 0");
}
TEST(ReplicaSetConfig, GleDefaultField) {
ReplicaSetConfig config;
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")) << "settings"
<< BSON("getLastErrorDefaults" << BSON("w"
<< "majority")))));
ASSERT_OK(config.validate());
ASSERT_EQUALS("majority", config.getDefaultWriteConcern().wMode);
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")) << "settings"
<< BSON("getLastErrorDefaults" << BSON("w"
<< "frim")))));
ASSERT_EQUALS(ErrorCodes::BadValue, config.validate());
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")) << "settings"
<< BSON("getLastErrorDefaults" << BSON("w" << 0)))));
ASSERT_EQUALS(ErrorCodes::BadValue, config.validate());
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "tags" << BSON("a"
<< "v")))
<< "settings" << BSON("getLastErrorDefaults"
<< BSON("w"
<< "frim") << "getLastErrorModes"
<< BSON("frim" << BSON("a" << 1))))));
ASSERT_OK(config.validate());
ASSERT_EQUALS("frim", config.getDefaultWriteConcern().wMode);
ASSERT_OK(config.findCustomWriteMode("frim").getStatus());
}
bool operator==(const MemberConfig& a, const MemberConfig& b) {
// do tag comparisons
for (MemberConfig::TagIterator itrA = a.tagsBegin(); itrA != a.tagsEnd(); ++itrA) {
if (std::find(b.tagsBegin(), b.tagsEnd(), *itrA) == b.tagsEnd()) {
return false;
}
}
return a.getId() == b.getId() && a.getHostAndPort() == b.getHostAndPort() &&
a.getPriority() == b.getPriority() && a.getSlaveDelay() == b.getSlaveDelay() &&
a.isVoter() == b.isVoter() && a.isArbiter() == b.isArbiter() &&
a.isHidden() == b.isHidden() && a.shouldBuildIndexes() == b.shouldBuildIndexes() &&
a.getNumTags() == b.getNumTags();
}
bool operator==(const ReplicaSetConfig& a, const ReplicaSetConfig& b) {
// compare WriteConcernModes
std::vector modeNames = a.getWriteConcernNames();
for (std::vector::iterator it = modeNames.begin(); it != modeNames.end(); it++) {
ReplicaSetTagPattern patternA = a.findCustomWriteMode(*it).getValue();
ReplicaSetTagPattern patternB = b.findCustomWriteMode(*it).getValue();
for (ReplicaSetTagPattern::ConstraintIterator itrA = patternA.constraintsBegin();
itrA != patternA.constraintsEnd();
itrA++) {
bool same = false;
for (ReplicaSetTagPattern::ConstraintIterator itrB = patternB.constraintsBegin();
itrB != patternB.constraintsEnd();
itrB++) {
if (itrA->getKeyIndex() == itrB->getKeyIndex() &&
itrA->getMinCount() == itrB->getMinCount()) {
same = true;
break;
}
}
if (!same) {
return false;
}
}
}
// compare the members
for (ReplicaSetConfig::MemberIterator memA = a.membersBegin(); memA != a.membersEnd(); memA++) {
bool same = false;
for (ReplicaSetConfig::MemberIterator memB = b.membersBegin(); memB != b.membersEnd();
memB++) {
if (*memA == *memB) {
same = true;
break;
}
}
if (!same) {
return false;
}
}
// simple comparisons
return a.getReplSetName() == b.getReplSetName() &&
a.getConfigVersion() == b.getConfigVersion() && a.getNumMembers() == b.getNumMembers() &&
a.getHeartbeatInterval() == b.getHeartbeatInterval() &&
a.getHeartbeatTimeoutPeriod() == b.getHeartbeatTimeoutPeriod() &&
a.getElectionTimeoutPeriod() == b.getElectionTimeoutPeriod() &&
a.isChainingAllowed() == b.isChainingAllowed() &&
a.isConfigServer() == b.isConfigServer() &&
a.getDefaultWriteConcern().wNumNodes == b.getDefaultWriteConcern().wNumNodes &&
a.getDefaultWriteConcern().wMode == b.getDefaultWriteConcern().wMode &&
a.getProtocolVersion() == b.getProtocolVersion() &&
a.getReplicaSetId() == b.getReplicaSetId();
}
TEST(ReplicaSetConfig, toBSONRoundTripAbility) {
ReplicaSetConfig configA;
ReplicaSetConfig configB;
ASSERT_OK(configA.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")) << "settings"
<< BSON("heartbeatIntervalMillis"
<< 5000 << "heartbeatTimeoutSecs" << 20
<< "replicaSetId" << OID::gen()))));
ASSERT_OK(configB.initialize(configA.toBSON()));
ASSERT_TRUE(configA == configB);
}
TEST(ReplicaSetConfig, toBSONRoundTripAbilityLarge) {
ReplicaSetConfig configA;
ReplicaSetConfig configB;
ASSERT_OK(configA.initialize(BSON(
"_id"
<< "asdf"
<< "version" << 9 << "writeConcernMajorityJournalDefault" << true << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "arbiterOnly" << true << "votes" << 1)
<< BSON("_id" << 3 << "host"
<< "localhost:3828"
<< "arbiterOnly" << false << "hidden" << true << "buildIndexes"
<< false << "priority" << 0 << "slaveDelay" << 17 << "votes"
<< 0 << "tags" << BSON("coast"
<< "east"
<< "ssd"
<< "true"))
<< BSON("_id" << 2 << "host"
<< "foo.com:3828"
<< "votes" << 0 << "priority" << 0 << "tags"
<< BSON("coast"
<< "west"
<< "hdd"
<< "true"))) << "protocolVersion" << 0 << "settings"
<< BSON("heartbeatIntervalMillis"
<< 5000 << "heartbeatTimeoutSecs" << 20 << "electionTimeoutMillis" << 4
<< "chainingAllowd" << true << "getLastErrorDefaults" << BSON("w"
<< "majority")
<< "getLastErrorModes" << BSON("disks" << BSON("ssd" << 1 << "hdd" << 1) << "coasts"
<< BSON("coast" << 2))))));
BSONObj configObjA = configA.toBSON();
// Ensure a protocolVersion does not show up if it is 0 to maintain cross version compatibility.
ASSERT_FALSE(configObjA.hasField("protocolVersion"));
ASSERT_OK(configB.initialize(configObjA));
ASSERT_TRUE(configA == configB);
}
TEST(ReplicaSetConfig, toBSONRoundTripAbilityInvalid) {
ReplicaSetConfig configA;
ReplicaSetConfig configB;
ASSERT_OK(configA.initialize(
BSON("_id"
<< ""
<< "version" << -3 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "arbiterOnly" << true << "votes" << 0 << "priority" << 0)
<< BSON("_id" << 0 << "host"
<< "localhost:3828"
<< "arbiterOnly" << false << "buildIndexes" << false
<< "priority" << 2)
<< BSON("_id" << 2 << "host"
<< "localhost:3828"
<< "votes" << 0 << "priority" << 0)) << "settings"
<< BSON("heartbeatIntervalMillis" << -5000 << "heartbeatTimeoutSecs" << 20
<< "electionTimeoutMillis" << 2))));
ASSERT_OK(configB.initialize(configA.toBSON()));
ASSERT_NOT_OK(configA.validate());
ASSERT_NOT_OK(configB.validate());
ASSERT_TRUE(configA == configB);
}
TEST(ReplicaSetConfig, CheckIfWriteConcernCanBeSatisfied) {
ReplicaSetConfig configA;
ASSERT_OK(configA.initialize(BSON(
"_id"
<< "rs0"
<< "version" << 1 << "members" << BSON_ARRAY(BSON("_id" << 0 << "host"
<< "node0"
<< "tags" << BSON("dc"
<< "NA"
<< "rack"
<< "rackNA1"))
<< BSON("_id" << 1 << "host"
<< "node1"
<< "tags" << BSON("dc"
<< "NA"
<< "rack"
<< "rackNA2"))
<< BSON("_id" << 2 << "host"
<< "node2"
<< "tags" << BSON("dc"
<< "NA"
<< "rack"
<< "rackNA3"))
<< BSON("_id" << 3 << "host"
<< "node3"
<< "tags" << BSON("dc"
<< "EU"
<< "rack"
<< "rackEU1"))
<< BSON("_id" << 4 << "host"
<< "node4"
<< "tags" << BSON("dc"
<< "EU"
<< "rack"
<< "rackEU2"))
<< BSON("_id" << 5 << "host"
<< "node5"
<< "arbiterOnly" << true))
<< "settings" << BSON("getLastErrorModes"
<< BSON("valid" << BSON("dc" << 2 << "rack" << 3)
<< "invalidNotEnoughValues" << BSON("dc" << 3)
<< "invalidNotEnoughNodes" << BSON("rack" << 6))))));
WriteConcernOptions validNumberWC;
validNumberWC.wNumNodes = 5;
ASSERT_OK(configA.checkIfWriteConcernCanBeSatisfied(validNumberWC));
WriteConcernOptions invalidNumberWC;
invalidNumberWC.wNumNodes = 6;
ASSERT_EQUALS(ErrorCodes::CannotSatisfyWriteConcern,
configA.checkIfWriteConcernCanBeSatisfied(invalidNumberWC));
WriteConcernOptions majorityWC;
majorityWC.wMode = "majority";
ASSERT_OK(configA.checkIfWriteConcernCanBeSatisfied(majorityWC));
WriteConcernOptions validModeWC;
validModeWC.wMode = "valid";
ASSERT_OK(configA.checkIfWriteConcernCanBeSatisfied(validModeWC));
WriteConcernOptions fakeModeWC;
fakeModeWC.wMode = "fake";
ASSERT_EQUALS(ErrorCodes::UnknownReplWriteConcern,
configA.checkIfWriteConcernCanBeSatisfied(fakeModeWC));
WriteConcernOptions invalidModeNotEnoughValuesWC;
invalidModeNotEnoughValuesWC.wMode = "invalidNotEnoughValues";
ASSERT_EQUALS(ErrorCodes::CannotSatisfyWriteConcern,
configA.checkIfWriteConcernCanBeSatisfied(invalidModeNotEnoughValuesWC));
WriteConcernOptions invalidModeNotEnoughNodesWC;
invalidModeNotEnoughNodesWC.wMode = "invalidNotEnoughNodes";
ASSERT_EQUALS(ErrorCodes::CannotSatisfyWriteConcern,
configA.checkIfWriteConcernCanBeSatisfied(invalidModeNotEnoughNodesWC));
}
TEST(ReplicaSetConfig, CheckMaximumNodesOkay) {
ReplicaSetConfig configA;
ReplicaSetConfig configB;
const int memberCount = 50;
ASSERT_OK(configA.initialize(createConfigDoc(memberCount)));
ASSERT_OK(configB.initialize(configA.toBSON()));
ASSERT_OK(configA.validate());
ASSERT_OK(configB.validate());
ASSERT_TRUE(configA == configB);
}
TEST(ReplicaSetConfig, CheckBeyondMaximumNodesFailsValidate) {
ReplicaSetConfig configA;
ReplicaSetConfig configB;
const int memberCount = 51;
ASSERT_OK(configA.initialize(createConfigDoc(memberCount)));
ASSERT_OK(configB.initialize(configA.toBSON()));
ASSERT_NOT_OK(configA.validate());
ASSERT_NOT_OK(configB.validate());
ASSERT_TRUE(configA == configB);
}
TEST(ReplicaSetConfig, CheckConfigServerCantBeProtocolVersion0) {
ReplicaSetConfig configA;
ASSERT_OK(configA.initialize(BSON("_id"
<< "rs0"
<< "protocolVersion" << 0 << "version" << 1 << "configsvr"
<< true << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")
<< BSON("_id" << 1 << "host"
<< "localhost:54321"
<< "arbiterOnly" << true)))));
Status status = configA.validate();
ASSERT_EQUALS(ErrorCodes::BadValue, status);
ASSERT_STRING_CONTAINS(status.reason(), "cannot run in protocolVersion 0");
}
TEST(ReplicaSetConfig, CheckConfigServerCantHaveArbiters) {
ReplicaSetConfig configA;
ASSERT_OK(configA.initialize(BSON("_id"
<< "rs0"
<< "protocolVersion" << 1 << "version" << 1 << "configsvr"
<< true << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")
<< BSON("_id" << 1 << "host"
<< "localhost:54321"
<< "arbiterOnly" << true)))));
Status status = configA.validate();
ASSERT_EQUALS(ErrorCodes::BadValue, status);
ASSERT_STRING_CONTAINS(status.reason(), "Arbiters are not allowed");
}
TEST(ReplicaSetConfig, CheckConfigServerMustBuildIndexes) {
ReplicaSetConfig configA;
ASSERT_OK(configA.initialize(BSON("_id"
<< "rs0"
<< "protocolVersion" << 1 << "version" << 1 << "configsvr"
<< true << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")
<< BSON("_id" << 1 << "host"
<< "localhost:54321"
<< "priority" << 0
<< "buildIndexes" << false)))));
Status status = configA.validate();
ASSERT_EQUALS(ErrorCodes::BadValue, status);
ASSERT_STRING_CONTAINS(status.reason(), "must build indexes");
}
TEST(ReplicaSetConfig, CheckConfigServerCantHaveSlaveDelay) {
ReplicaSetConfig configA;
ASSERT_OK(
configA.initialize(BSON("_id"
<< "rs0"
<< "protocolVersion" << 1 << "version" << 1 << "configsvr" << true
<< "members" << BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")
<< BSON("_id" << 1 << "host"
<< "localhost:54321"
<< "priority" << 0
<< "slaveDelay" << 3)))));
Status status = configA.validate();
ASSERT_EQUALS(ErrorCodes::BadValue, status);
ASSERT_STRING_CONTAINS(status.reason(), "cannot have a non-zero slaveDelay");
}
TEST(ReplicaSetConfig, CheckConfigServerMustHaveTrueForWriteConcernMajorityJournalDefault) {
serverGlobalParams.configsvr = true;
ON_BLOCK_EXIT([&] { serverGlobalParams.configsvr = false; });
ReplicaSetConfig configA;
ASSERT_OK(
configA.initialize(BSON("_id"
<< "rs0"
<< "protocolVersion" << 1 << "version" << 1 << "configsvr" << true
<< "members" << BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")
<< BSON("_id" << 1 << "host"
<< "localhost:54321"))
<< "writeConcernMajorityJournalDefault" << false)));
Status status = configA.validate();
ASSERT_EQUALS(ErrorCodes::BadValue, status);
ASSERT_STRING_CONTAINS(status.reason(), " must be true in replica set configurations being ");
}
TEST(ReplicaSetConfig, GetPriorityTakeoverDelay) {
ReplicaSetConfig configA;
ASSERT_OK(configA.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "priority" << 1)
<< BSON("_id" << 1 << "host"
<< "localhost:54321"
<< "priority" << 2)
<< BSON("_id" << 2 << "host"
<< "localhost:5321"
<< "priority" << 3)
<< BSON("_id" << 3 << "host"
<< "localhost:5421"
<< "priority" << 4)
<< BSON("_id" << 4 << "host"
<< "localhost:5431"
<< "priority" << 5)) << "settings"
<< BSON("electionTimeoutMillis" << 1000))));
ASSERT_OK(configA.validate());
ASSERT_EQUALS(Milliseconds(5000), configA.getPriorityTakeoverDelay(0));
ASSERT_EQUALS(Milliseconds(4000), configA.getPriorityTakeoverDelay(1));
ASSERT_EQUALS(Milliseconds(3000), configA.getPriorityTakeoverDelay(2));
ASSERT_EQUALS(Milliseconds(2000), configA.getPriorityTakeoverDelay(3));
ASSERT_EQUALS(Milliseconds(1000), configA.getPriorityTakeoverDelay(4));
ReplicaSetConfig configB;
ASSERT_OK(configB.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "priority" << 1)
<< BSON("_id" << 1 << "host"
<< "localhost:54321"
<< "priority" << 2)
<< BSON("_id" << 2 << "host"
<< "localhost:5321"
<< "priority" << 2)
<< BSON("_id" << 3 << "host"
<< "localhost:5421"
<< "priority" << 3)
<< BSON("_id" << 4 << "host"
<< "localhost:5431"
<< "priority" << 3)) << "settings"
<< BSON("electionTimeoutMillis" << 1000))));
ASSERT_OK(configB.validate());
ASSERT_EQUALS(Milliseconds(5000), configB.getPriorityTakeoverDelay(0));
ASSERT_EQUALS(Milliseconds(3000), configB.getPriorityTakeoverDelay(1));
ASSERT_EQUALS(Milliseconds(3000), configB.getPriorityTakeoverDelay(2));
ASSERT_EQUALS(Milliseconds(1000), configB.getPriorityTakeoverDelay(3));
ASSERT_EQUALS(Milliseconds(1000), configB.getPriorityTakeoverDelay(4));
}
TEST(ReplicaSetConfig, ConfirmDefaultValuesOfAndAbilityToSetWriteConcernMajorityJournalDefault) {
// PV0, should default to false.
ReplicaSetConfig config;
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
ASSERT_OK(config.validate());
ASSERT_FALSE(config.getWriteConcernMajorityShouldJournal());
ASSERT_FALSE(config.toBSON().hasField("writeConcernMajorityJournalDefault"));
// Should be able to set it true in PV0.
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"))
<< "writeConcernMajorityJournalDefault" << true)));
ASSERT_OK(config.validate());
ASSERT_TRUE(config.getWriteConcernMajorityShouldJournal());
ASSERT_TRUE(config.toBSON().hasField("writeConcernMajorityJournalDefault"));
// PV1, should default to true.
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "protocolVersion" << 1 << "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345")))));
ASSERT_OK(config.validate());
ASSERT_TRUE(config.getWriteConcernMajorityShouldJournal());
ASSERT_FALSE(config.toBSON().hasField("writeConcernMajorityJournalDefault"));
// Should be able to set it false in PV1.
ASSERT_OK(config.initialize(BSON("_id"
<< "rs0"
<< "protocolVersion" << 1 << "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"))
<< "writeConcernMajorityJournalDefault" << false)));
ASSERT_OK(config.validate());
ASSERT_FALSE(config.getWriteConcernMajorityShouldJournal());
ASSERT_TRUE(config.toBSON().hasField("writeConcernMajorityJournalDefault"));
}
TEST(ReplicaSetConfig, ReplSetId) {
// Uninitialized configuration has no ID.
ASSERT_FALSE(ReplicaSetConfig().hasReplicaSetId());
// Cannot provide replica set ID in configuration document when initialized from
// replSetInitiate.
auto status =
ReplicaSetConfig().initializeForInitiate(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "priority" << 1))
<< "settings"
<< BSON("replicaSetId" << OID::gen())));
ASSERT_EQUALS(ErrorCodes::InvalidReplicaSetConfig, status);
ASSERT_STRING_CONTAINS(status.reason(),
"replica set configuration cannot contain 'replicaSetId' field when "
"called from replSetInitiate");
// Configuration created by replSetInitiate should generate replica set ID.
ReplicaSetConfig configInitiate;
ASSERT_OK(
configInitiate.initializeForInitiate(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "priority" << 1)))));
ASSERT_OK(configInitiate.validate());
ASSERT_TRUE(configInitiate.hasReplicaSetId());
OID replicaSetId = configInitiate.getReplicaSetId();
// Configuration initialized from local database can contain ID.
ReplicaSetConfig configLocal;
ASSERT_OK(configLocal.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "priority" << 1))
<< "settings" << BSON("replicaSetId" << replicaSetId))));
ASSERT_OK(configLocal.validate());
ASSERT_TRUE(configLocal.hasReplicaSetId());
ASSERT_EQUALS(replicaSetId, configLocal.getReplicaSetId());
// When reconfiguring, we can provide an default ID if the configuration does not contain one.
OID defaultReplicaSetId = OID::gen();
ASSERT_OK(configLocal.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "priority" << 1))),
true,
defaultReplicaSetId));
ASSERT_OK(configLocal.validate());
ASSERT_TRUE(configLocal.hasReplicaSetId());
ASSERT_EQUALS(defaultReplicaSetId, configLocal.getReplicaSetId());
// 'replicaSetId' field cannot be null.
status = configLocal.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "priority" << 1)) << "settings"
<< BSON("replicaSetId" << OID())));
ASSERT_EQUALS(ErrorCodes::BadValue, status);
ASSERT_STRING_CONTAINS(status.reason(), "replicaSetId field value cannot be null");
// 'replicaSetId' field must be an OID.
status = configLocal.initialize(BSON("_id"
<< "rs0"
<< "version" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"
<< "priority" << 1)) << "settings"
<< BSON("replicaSetId" << 12345)));
ASSERT_EQUALS(ErrorCodes::TypeMismatch, status);
ASSERT_STRING_CONTAINS(status.reason(),
"\"replicaSetId\" had the wrong type. Expected OID, found NumberInt32");
}
} // namespace
} // namespace repl
} // namespace mongo