summaryrefslogtreecommitdiff
path: root/src/mongo/db/repl/replica_set_config_checks_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/repl/replica_set_config_checks_test.cpp')
-rw-r--r--src/mongo/db/repl/replica_set_config_checks_test.cpp1327
1 files changed, 673 insertions, 654 deletions
diff --git a/src/mongo/db/repl/replica_set_config_checks_test.cpp b/src/mongo/db/repl/replica_set_config_checks_test.cpp
index efb39f5e0fa..d495421689d 100644
--- a/src/mongo/db/repl/replica_set_config_checks_test.cpp
+++ b/src/mongo/db/repl/replica_set_config_checks_test.cpp
@@ -40,660 +40,679 @@ namespace mongo {
namespace repl {
namespace {
- TEST(ValidateConfigForInitiate, VersionMustBe1) {
- ReplicationCoordinatorExternalStateMock rses;
- rses.addSelf(HostAndPort("h1"));
-
- ReplicaSetConfig config;
- ASSERT_OK(config.initialize(BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1")))));
- ASSERT_EQUALS(
- ErrorCodes::NewReplicaSetConfigurationIncompatible,
- validateConfigForInitiate(&rses, config).getStatus());
- }
-
- TEST(ValidateConfigForInitiate, MustFindSelf) {
- ReplicaSetConfig config;
- ASSERT_OK(config.initialize(BSON("_id" << "rs0" <<
- "version" << 1 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2") <<
- BSON("_id" << 3 << "host" << "h3")))));
- ReplicationCoordinatorExternalStateMock notPresentExternalState;
- ReplicationCoordinatorExternalStateMock presentOnceExternalState;
- presentOnceExternalState.addSelf(HostAndPort("h2"));
- ReplicationCoordinatorExternalStateMock presentTwiceExternalState;
- presentTwiceExternalState.addSelf(HostAndPort("h3"));
- presentTwiceExternalState.addSelf(HostAndPort("h1"));
-
- ASSERT_EQUALS(ErrorCodes::NodeNotFound,
- validateConfigForInitiate(&notPresentExternalState, config).getStatus());
- ASSERT_EQUALS(ErrorCodes::DuplicateKey,
- validateConfigForInitiate(&presentTwiceExternalState, config).getStatus());
- ASSERT_EQUALS(1, unittest::assertGet(validateConfigForInitiate(&presentOnceExternalState,
- config)));
- }
-
- TEST(ValidateConfigForInitiate, SelfMustBeElectable) {
- ReplicaSetConfig config;
- ASSERT_OK(config.initialize(BSON("_id" << "rs0" <<
- "version" << 1 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2" <<
- "priority" << 0) <<
- BSON("_id" << 3 << "host" << "h3")))));
- ReplicationCoordinatorExternalStateMock presentOnceExternalState;
- presentOnceExternalState.addSelf(HostAndPort("h2"));
-
- ASSERT_EQUALS(ErrorCodes::NodeNotElectable,
- validateConfigForInitiate(&presentOnceExternalState, config).getStatus());
- }
-
- TEST(ValidateConfigForReconfig, NewConfigVersionNumberMustBeHigherThanOld) {
- ReplicationCoordinatorExternalStateMock externalState;
- externalState.addSelf(HostAndPort("h1"));
-
- ReplicaSetConfig oldConfig;
- ReplicaSetConfig newConfig;
-
- // Two configurations, identical except for version.
- ASSERT_OK(oldConfig.initialize(
- BSON("_id" << "rs0" <<
- "version" << 1 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2") <<
- BSON("_id" << 3 << "host" << "h3")))));
-
- ASSERT_OK(newConfig.initialize(
- BSON("_id" << "rs0" <<
- "version" << 3 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2") <<
- BSON("_id" << 3 << "host" << "h3")))));
-
- ASSERT_OK(oldConfig.validate());
- ASSERT_OK(newConfig.validate());
-
- // Can reconfig from old to new.
- ASSERT_OK(validateConfigForReconfig(&externalState,
- oldConfig,
- newConfig,
- false).getStatus());
-
-
- // Cannot reconfig from old to old (versions must be different).
- ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible,
- validateConfigForReconfig(&externalState,
- oldConfig,
- oldConfig,
- false).getStatus());
- // Forced reconfigs also do not allow this.
- ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible,
- validateConfigForReconfig(&externalState,
- oldConfig,
- oldConfig,
- true).getStatus());
-
- // Cannot reconfig from new to old (versions must increase).
- ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible,
- validateConfigForReconfig(&externalState,
- newConfig,
- oldConfig,
- false).getStatus());
- // Forced reconfigs also do not allow this.
- ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible,
- validateConfigForReconfig(&externalState,
- newConfig,
- oldConfig,
- true).getStatus());
- }
-
- TEST(ValidateConfigForReconfig, NewConfigMustNotChangeSetName) {
- ReplicationCoordinatorExternalStateMock externalState;
- externalState.addSelf(HostAndPort("h1"));
-
- ReplicaSetConfig oldConfig;
- ReplicaSetConfig newConfig;
-
- // Two configurations, compatible except for set name.
- ASSERT_OK(oldConfig.initialize(
- BSON("_id" << "rs0" <<
- "version" << 1 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2") <<
- BSON("_id" << 3 << "host" << "h3")))));
-
- ASSERT_OK(newConfig.initialize(
- BSON("_id" << "rs1" <<
- "version" << 3 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2") <<
- BSON("_id" << 3 << "host" << "h3")))));
-
- ASSERT_OK(oldConfig.validate());
- ASSERT_OK(newConfig.validate());
- ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible,
- validateConfigForReconfig(&externalState,
- oldConfig,
- newConfig,
- false).getStatus());
- // Forced reconfigs also do not allow this.
- ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible,
- validateConfigForReconfig(&externalState,
- newConfig,
- oldConfig,
- true).getStatus());
- }
-
- TEST(ValidateConfigForReconfig, NewConfigMustNotFlipBuildIndexesFlag) {
- ReplicationCoordinatorExternalStateMock externalState;
- externalState.addSelf(HostAndPort("h1"));
-
- ReplicaSetConfig oldConfig;
- ReplicaSetConfig newConfig;
- ReplicaSetConfig oldConfigRefresh;
-
- // Three configurations, two compatible except that h2 flips the buildIndex flag.
- // The third, compatible with the first.
- ASSERT_OK(oldConfig.initialize(
- BSON("_id" << "rs0" <<
- "version" << 1 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2" <<
- "buildIndexes" << false <<
- "priority" << 0) <<
- BSON("_id" << 3 << "host" << "h3")))));
-
- ASSERT_OK(newConfig.initialize(
- BSON("_id" << "rs0" <<
- "version" << 3 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2" <<
- "buildIndexes" << true <<
- "priority" << 0) <<
- BSON("_id" << 3 << "host" << "h3")))));
-
- ASSERT_OK(oldConfigRefresh.initialize(
- BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2" <<
- "buildIndexes" << false <<
- "priority" << 0) <<
- BSON("_id" << 3 << "host" << "h3")))));
-
- ASSERT_OK(oldConfig.validate());
- ASSERT_OK(newConfig.validate());
- ASSERT_OK(oldConfigRefresh.validate());
- ASSERT_OK(validateConfigForReconfig(&externalState,
- oldConfig,
- oldConfigRefresh,
- false).getStatus());
- ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible,
- validateConfigForReconfig(&externalState,
- oldConfig,
- newConfig,
- false).getStatus());
-
- // Forced reconfigs also do not allow this.
- ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible,
- validateConfigForReconfig(&externalState,
- oldConfig,
- newConfig,
- true).getStatus());
- }
-
- TEST(ValidateConfigForReconfig, NewConfigMustNotFlipArbiterFlag) {
- ReplicationCoordinatorExternalStateMock externalState;
- externalState.addSelf(HostAndPort("h1"));
-
- ReplicaSetConfig oldConfig;
- ReplicaSetConfig newConfig;
- ReplicaSetConfig oldConfigRefresh;
-
- // Three configurations, two compatible except that h2 flips the arbiterOnly flag.
- // The third, compatible with the first.
- ASSERT_OK(oldConfig.initialize(
- BSON("_id" << "rs0" <<
- "version" << 1 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2" <<
- "arbiterOnly" << false) <<
- BSON("_id" << 3 << "host" << "h3")))));
-
- ASSERT_OK(newConfig.initialize(
- BSON("_id" << "rs0" <<
- "version" << 3 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2" <<
- "arbiterOnly" << true) <<
- BSON("_id" << 3 << "host" << "h3")))));
-
- ASSERT_OK(oldConfigRefresh.initialize(
- BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2" <<
- "arbiterOnly" << false) <<
- BSON("_id" << 3 << "host" << "h3")))));
-
- ASSERT_OK(oldConfig.validate());
- ASSERT_OK(newConfig.validate());
- ASSERT_OK(oldConfigRefresh.validate());
- ASSERT_OK(validateConfigForReconfig(&externalState,
- oldConfig,
- oldConfigRefresh,
- false).getStatus());
- ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible,
- validateConfigForReconfig(&externalState,
- oldConfig,
- newConfig,
- false).getStatus());
- // Forced reconfigs also do not allow this.
- ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible,
- validateConfigForReconfig(&externalState,
- oldConfig,
- newConfig,
- true).getStatus());
- }
-
- TEST(ValidateConfigForReconfig, HostAndIdRemappingRestricted) {
- // When reconfiguring a replica set, it is allowed to introduce (host, id) pairs
- // absent from the old config only when the hosts and ids were both individually
- // absent in the old config.
-
- ReplicationCoordinatorExternalStateMock externalState;
- externalState.addSelf(HostAndPort("h1"));
-
- ReplicaSetConfig oldConfig;
- ReplicaSetConfig legalNewConfigWithNewHostAndId;
- ReplicaSetConfig illegalNewConfigReusingHost;
- ReplicaSetConfig illegalNewConfigReusingId;
-
- ASSERT_OK(oldConfig.initialize(
- BSON("_id" << "rs0" <<
- "version" << 1 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2") <<
- BSON("_id" << 3 << "host" << "h3")))));
- ASSERT_OK(oldConfig.validate());
-
- //
- // Here, the new config is valid because we've replaced (2, "h2") with
- // (4, "h4"), so neither the member _id or host name were reused.
- //
- ASSERT_OK(legalNewConfigWithNewHostAndId.initialize(
- BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 4 << "host" << "h4") <<
- BSON("_id" << 3 << "host" << "h3")))));
- ASSERT_OK(legalNewConfigWithNewHostAndId.validate());
- ASSERT_OK(validateConfigForReconfig(&externalState,
- oldConfig,
- legalNewConfigWithNewHostAndId,
- false).getStatus());
-
- //
- // Here, the new config is invalid because we've reused host name "h2" with
- // new _id 4.
- //
- ASSERT_OK(illegalNewConfigReusingHost.initialize(
- BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 4 << "host" << "h2") <<
- BSON("_id" << 3 << "host" << "h3")))));
- ASSERT_OK(illegalNewConfigReusingHost.validate());
- ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible,
- validateConfigForReconfig(&externalState,
- oldConfig,
- illegalNewConfigReusingHost,
- false).getStatus());
- // Forced reconfigs also do not allow this.
- ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible,
- validateConfigForReconfig(&externalState,
- oldConfig,
- illegalNewConfigReusingHost,
- true).getStatus());
- //
- // Here, the new config is valid, because all we've changed is the name of
- // the host representing _id 2.
- //
- ASSERT_OK(illegalNewConfigReusingId.initialize(
- BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h4") <<
- BSON("_id" << 3 << "host" << "h3")))));
- ASSERT_OK(illegalNewConfigReusingId.validate());
- ASSERT_OK(validateConfigForReconfig(&externalState,
- oldConfig,
- illegalNewConfigReusingId,
- false).getStatus());
- }
-
- TEST(ValidateConfigForReconfig, MustFindSelf) {
- // Old and new config are same except for version change; this is just testing that we can
- // find ourself in the new config.
- ReplicaSetConfig oldConfig;
- ASSERT_OK(oldConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 1 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2") <<
- BSON("_id" << 3 << "host" << "h3")))));
-
- ReplicaSetConfig newConfig;
- ASSERT_OK(newConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2") <<
- BSON("_id" << 3 << "host" << "h3")))));
- ReplicationCoordinatorExternalStateMock notPresentExternalState;
- ReplicationCoordinatorExternalStateMock presentOnceExternalState;
- presentOnceExternalState.addSelf(HostAndPort("h2"));
- ReplicationCoordinatorExternalStateMock presentThriceExternalState;
- presentThriceExternalState.addSelf(HostAndPort("h3"));
- presentThriceExternalState.addSelf(HostAndPort("h2"));
- presentThriceExternalState.addSelf(HostAndPort("h1"));
-
- ASSERT_EQUALS(ErrorCodes::NodeNotFound,
- validateConfigForReconfig(&notPresentExternalState,
- oldConfig,
- newConfig,
- false).getStatus());
- ASSERT_EQUALS(ErrorCodes::DuplicateKey,
- validateConfigForReconfig(&presentThriceExternalState,
- oldConfig,
- newConfig,
- false).getStatus());
- ASSERT_EQUALS(1, unittest::assertGet(validateConfigForReconfig(&presentOnceExternalState,
- oldConfig,
- newConfig,
- false)));
- // Forced reconfigs also do not allow this.
- ASSERT_EQUALS(ErrorCodes::NodeNotFound,
- validateConfigForReconfig(&notPresentExternalState,
- oldConfig,
- newConfig,
- true).getStatus());
- ASSERT_EQUALS(ErrorCodes::DuplicateKey,
- validateConfigForReconfig(&presentThriceExternalState,
- oldConfig,
- newConfig,
- true).getStatus());
- ASSERT_EQUALS(1, unittest::assertGet(validateConfigForReconfig(&presentOnceExternalState,
- oldConfig,
- newConfig,
- true)));
- }
-
- TEST(ValidateConfigForReconfig, SelfMustEndElectable) {
- // Old and new config are same except for version change and the electability of one node;
- // this is just testing that we must be electable in the new config.
- ReplicaSetConfig oldConfig;
- ASSERT_OK(oldConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 1 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2") <<
- BSON("_id" << 3 << "host" << "h3")))));
-
- ReplicaSetConfig newConfig;
- ASSERT_OK(newConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h1") <<
- BSON("_id" << 2 << "host" << "h2" <<
- "priority" << 0) <<
- BSON("_id" << 3 << "host" << "h3")))));
- ReplicationCoordinatorExternalStateMock presentOnceExternalState;
- presentOnceExternalState.addSelf(HostAndPort("h2"));
-
- ASSERT_EQUALS(ErrorCodes::NodeNotElectable,
- validateConfigForReconfig(&presentOnceExternalState,
- oldConfig,
- newConfig,
- false).getStatus());
- // Forced reconfig does not require electability.
- ASSERT_OK(validateConfigForReconfig(&presentOnceExternalState,
- oldConfig,
- newConfig,
- true).getStatus());
- }
-
- TEST(ValidateConfigForInitiate, NewConfigInvalid) {
- // The new config is not valid due to a duplicate _id value. This tests that if the new
- // config is invalid, validateConfigForInitiate will return a status indicating what is
- // wrong with the new config.
- ReplicaSetConfig newConfig;
- ASSERT_OK(newConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 0 << "host" << "h2") <<
- BSON("_id" << 0 << "host" << "h3")))));
-
- ReplicationCoordinatorExternalStateMock presentOnceExternalState;
- presentOnceExternalState.addSelf(HostAndPort("h2"));
- ASSERT_EQUALS(ErrorCodes::BadValue, validateConfigForInitiate(&presentOnceExternalState,
- newConfig).getStatus());
- }
-
- TEST(ValidateConfigForReconfig, NewConfigInvalid) {
- // The new config is not valid due to a duplicate _id value. This tests that if the new
- // config is invalid, validateConfigForReconfig will return a status indicating what is
- // wrong with the new config.
- ReplicaSetConfig oldConfig;
- ASSERT_OK(oldConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 1 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 0 << "host" << "h2")))));
-
- ReplicaSetConfig newConfig;
- ASSERT_OK(newConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 0 << "host" << "h2") <<
- BSON("_id" << 0 << "host" << "h3")))));
-
- ReplicationCoordinatorExternalStateMock presentOnceExternalState;
- presentOnceExternalState.addSelf(HostAndPort("h2"));
- ASSERT_EQUALS(ErrorCodes::BadValue, validateConfigForReconfig(&presentOnceExternalState,
- oldConfig,
- newConfig,
- false).getStatus());
- // Forced reconfigs also do not allow this.
- ASSERT_EQUALS(ErrorCodes::BadValue, validateConfigForReconfig(&presentOnceExternalState,
- oldConfig,
- newConfig,
- true).getStatus());
- }
-
- TEST(ValidateConfigForStartUp, NewConfigInvalid) {
- // The new config is not valid due to a duplicate _id value. This tests that if the new
- // config is invalid, validateConfigForStartUp will return a status indicating what is wrong
- // with the new config.
- ReplicaSetConfig oldConfig;
- ASSERT_OK(oldConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 1 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 0 << "host" << "h2")))));
-
- ReplicaSetConfig newConfig;
- ASSERT_OK(newConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 0 << "host" << "h2") <<
- BSON("_id" << 0 << "host" << "h3")))));
-
- ReplicationCoordinatorExternalStateMock presentOnceExternalState;
- presentOnceExternalState.addSelf(HostAndPort("h2"));
- ASSERT_EQUALS(ErrorCodes::BadValue, validateConfigForStartUp(&presentOnceExternalState,
- oldConfig,
- newConfig).getStatus());
- }
-
- TEST(ValidateConfigForStartUp, OldAndNewConfigIncompatible) {
- // The new config is not compatible with the old config due to a member changing _ids. This
- // tests that validateConfigForStartUp will return a status indicating the incompatiblilty
- // between the old and new config.
- ReplicaSetConfig oldConfig;
- ASSERT_OK(oldConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 1 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 0 << "host" << "h2") <<
- BSON("_id" << 1 << "host" << "h3")))));
-
-
- ReplicaSetConfig newConfig;
- ASSERT_OK(newConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 2 << "host" << "h2") <<
- BSON("_id" << 1 << "host" << "h3")))));
-
- ReplicationCoordinatorExternalStateMock presentOnceExternalState;
- presentOnceExternalState.addSelf(HostAndPort("h2"));
- ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible,
- validateConfigForStartUp(&presentOnceExternalState,
- oldConfig,
- newConfig).getStatus());
- }
-
- TEST(ValidateConfigForStartUp, OldAndNewConfigCompatible) {
- // The new config is compatible with the old config. This tests that
- // validateConfigForStartUp will return a Status::OK() indicating the validity of this
- // config change.
- ReplicaSetConfig oldConfig;
- ASSERT_OK(oldConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 1 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 0 << "host" << "h2") <<
- BSON("_id" << 1 << "host" << "h3")))));
-
-
- ReplicaSetConfig newConfig;
- ASSERT_OK(newConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 0 << "host" << "h2" <<
- "priority" << 3) <<
- BSON("_id" << 1 << "host" << "h3")))));
-
- ReplicationCoordinatorExternalStateMock presentOnceExternalState;
- presentOnceExternalState.addSelf(HostAndPort("h2"));
- ASSERT_OK(validateConfigForStartUp(&presentOnceExternalState,
- oldConfig,
- newConfig).getStatus());
- }
-
- TEST(ValidateConfigForHeartbeatReconfig, NewConfigInvalid) {
- // The new config is not valid due to a duplicate _id value. This tests that if the new
- // config is invalid, validateConfigForHeartbeatReconfig will return a status indicating
- // what is wrong with the new config.
- ReplicaSetConfig newConfig;
- ASSERT_OK(newConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 0 << "host" << "h2") <<
- BSON("_id" << 0 << "host" << "h3")))));
-
- ReplicationCoordinatorExternalStateMock presentOnceExternalState;
- presentOnceExternalState.addSelf(HostAndPort("h2"));
- ASSERT_EQUALS(ErrorCodes::BadValue,
- validateConfigForHeartbeatReconfig(&presentOnceExternalState,
- newConfig).getStatus());
- }
-
- TEST(ValidateConfigForHeartbeatReconfig, NewConfigValid) {
- // The new config is valid. This tests that validateConfigForHeartbeatReconfig will return
- // a Status::OK() indicating the validity of this config change.
- ReplicaSetConfig newConfig;
- ASSERT_OK(newConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 0 << "host" << "h2") <<
- BSON("_id" << 1 << "host" << "h3")))));
-
- ReplicationCoordinatorExternalStateMock presentOnceExternalState;
- presentOnceExternalState.addSelf(HostAndPort("h2"));
- ASSERT_OK(validateConfigForHeartbeatReconfig(&presentOnceExternalState,
- newConfig).getStatus());
- }
-
- TEST(ValidateForReconfig, ForceStillNeedsValidConfig) {
- // The new config is invalid due to two nodes with the same _id value. This tests that
- // ValidateForReconfig fails with an invalid config, even if force is true.
- ReplicaSetConfig oldConfig;
- ASSERT_OK(oldConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 1 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 0 << "host" << "h2") <<
- BSON("_id" << 1 << "host" << "h3")))));
-
-
- ReplicaSetConfig newConfig;
- ASSERT_OK(newConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 0 << "host" << "h2") <<
- BSON("_id" << 0 << "host" << "h3")))));
-
- ReplicationCoordinatorExternalStateMock presentOnceExternalState;
- presentOnceExternalState.addSelf(HostAndPort("h2"));
- ASSERT_EQUALS(ErrorCodes::BadValue,
- validateConfigForReconfig(&presentOnceExternalState,
- oldConfig,
- newConfig,
- true).getStatus());
- }
-
- TEST(ValidateForReconfig, ForceStillNeedsSelfPresent) {
- // The new config does not contain self. This tests that ValidateForReconfig fails
- // if the member receiving it is absent from the config, even if force is true.
- ReplicaSetConfig oldConfig;
- ASSERT_OK(oldConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 1 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 0 << "host" << "h2") <<
- BSON("_id" << 1 << "host" << "h3")))));
-
-
- ReplicaSetConfig newConfig;
- ASSERT_OK(newConfig.initialize(BSON("_id" << "rs0" <<
- "version" << 2 <<
- "members" << BSON_ARRAY(
- BSON("_id" << 1 << "host" << "h3") <<
- BSON("_id" << 2 << "host" << "h4")))));
-
- ReplicationCoordinatorExternalStateMock presentOnceExternalState;
- presentOnceExternalState.addSelf(HostAndPort("h2"));
- ASSERT_EQUALS(ErrorCodes::NodeNotFound,
- validateConfigForReconfig(&presentOnceExternalState,
- oldConfig,
- newConfig,
- true).getStatus());
- }
+TEST(ValidateConfigForInitiate, VersionMustBe1) {
+ ReplicationCoordinatorExternalStateMock rses;
+ rses.addSelf(HostAndPort("h1"));
+
+ ReplicaSetConfig config;
+ ASSERT_OK(config.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")))));
+ ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible,
+ validateConfigForInitiate(&rses, config).getStatus());
+}
+
+TEST(ValidateConfigForInitiate, MustFindSelf) {
+ ReplicaSetConfig config;
+ ASSERT_OK(
+ config.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2") << BSON("_id" << 3 << "host"
+ << "h3")))));
+ ReplicationCoordinatorExternalStateMock notPresentExternalState;
+ ReplicationCoordinatorExternalStateMock presentOnceExternalState;
+ presentOnceExternalState.addSelf(HostAndPort("h2"));
+ ReplicationCoordinatorExternalStateMock presentTwiceExternalState;
+ presentTwiceExternalState.addSelf(HostAndPort("h3"));
+ presentTwiceExternalState.addSelf(HostAndPort("h1"));
+
+ ASSERT_EQUALS(ErrorCodes::NodeNotFound,
+ validateConfigForInitiate(&notPresentExternalState, config).getStatus());
+ ASSERT_EQUALS(ErrorCodes::DuplicateKey,
+ validateConfigForInitiate(&presentTwiceExternalState, config).getStatus());
+ ASSERT_EQUALS(
+ 1, unittest::assertGet(validateConfigForInitiate(&presentOnceExternalState, config)));
+}
+
+TEST(ValidateConfigForInitiate, SelfMustBeElectable) {
+ ReplicaSetConfig config;
+ ASSERT_OK(config.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2"
+ << "priority" << 0)
+ << BSON("_id" << 3 << "host"
+ << "h3")))));
+ ReplicationCoordinatorExternalStateMock presentOnceExternalState;
+ presentOnceExternalState.addSelf(HostAndPort("h2"));
+
+ ASSERT_EQUALS(ErrorCodes::NodeNotElectable,
+ validateConfigForInitiate(&presentOnceExternalState, config).getStatus());
+}
+
+TEST(ValidateConfigForReconfig, NewConfigVersionNumberMustBeHigherThanOld) {
+ ReplicationCoordinatorExternalStateMock externalState;
+ externalState.addSelf(HostAndPort("h1"));
+
+ ReplicaSetConfig oldConfig;
+ ReplicaSetConfig newConfig;
+
+ // Two configurations, identical except for version.
+ ASSERT_OK(
+ oldConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2") << BSON("_id" << 3 << "host"
+ << "h3")))));
+
+ ASSERT_OK(
+ newConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 3 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2") << BSON("_id" << 3 << "host"
+ << "h3")))));
+
+ ASSERT_OK(oldConfig.validate());
+ ASSERT_OK(newConfig.validate());
+
+ // Can reconfig from old to new.
+ ASSERT_OK(validateConfigForReconfig(&externalState, oldConfig, newConfig, false).getStatus());
+
+
+ // Cannot reconfig from old to old (versions must be different).
+ ASSERT_EQUALS(
+ ErrorCodes::NewReplicaSetConfigurationIncompatible,
+ validateConfigForReconfig(&externalState, oldConfig, oldConfig, false).getStatus());
+ // Forced reconfigs also do not allow this.
+ ASSERT_EQUALS(
+ ErrorCodes::NewReplicaSetConfigurationIncompatible,
+ validateConfigForReconfig(&externalState, oldConfig, oldConfig, true).getStatus());
+
+ // Cannot reconfig from new to old (versions must increase).
+ ASSERT_EQUALS(
+ ErrorCodes::NewReplicaSetConfigurationIncompatible,
+ validateConfigForReconfig(&externalState, newConfig, oldConfig, false).getStatus());
+ // Forced reconfigs also do not allow this.
+ ASSERT_EQUALS(
+ ErrorCodes::NewReplicaSetConfigurationIncompatible,
+ validateConfigForReconfig(&externalState, newConfig, oldConfig, true).getStatus());
+}
+
+TEST(ValidateConfigForReconfig, NewConfigMustNotChangeSetName) {
+ ReplicationCoordinatorExternalStateMock externalState;
+ externalState.addSelf(HostAndPort("h1"));
+
+ ReplicaSetConfig oldConfig;
+ ReplicaSetConfig newConfig;
+
+ // Two configurations, compatible except for set name.
+ ASSERT_OK(
+ oldConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2") << BSON("_id" << 3 << "host"
+ << "h3")))));
+
+ ASSERT_OK(
+ newConfig.initialize(BSON("_id"
+ << "rs1"
+ << "version" << 3 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2") << BSON("_id" << 3 << "host"
+ << "h3")))));
+
+ ASSERT_OK(oldConfig.validate());
+ ASSERT_OK(newConfig.validate());
+ ASSERT_EQUALS(
+ ErrorCodes::NewReplicaSetConfigurationIncompatible,
+ validateConfigForReconfig(&externalState, oldConfig, newConfig, false).getStatus());
+ // Forced reconfigs also do not allow this.
+ ASSERT_EQUALS(
+ ErrorCodes::NewReplicaSetConfigurationIncompatible,
+ validateConfigForReconfig(&externalState, newConfig, oldConfig, true).getStatus());
+}
+
+TEST(ValidateConfigForReconfig, NewConfigMustNotFlipBuildIndexesFlag) {
+ ReplicationCoordinatorExternalStateMock externalState;
+ externalState.addSelf(HostAndPort("h1"));
+
+ ReplicaSetConfig oldConfig;
+ ReplicaSetConfig newConfig;
+ ReplicaSetConfig oldConfigRefresh;
+
+ // Three configurations, two compatible except that h2 flips the buildIndex flag.
+ // The third, compatible with the first.
+ ASSERT_OK(oldConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2"
+ << "buildIndexes" << false
+ << "priority" << 0)
+ << BSON("_id" << 3 << "host"
+ << "h3")))));
+
+ ASSERT_OK(newConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 3 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2"
+ << "buildIndexes" << true
+ << "priority" << 0)
+ << BSON("_id" << 3 << "host"
+ << "h3")))));
+
+ ASSERT_OK(
+ oldConfigRefresh.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2"
+ << "buildIndexes" << false
+ << "priority" << 0)
+ << BSON("_id" << 3 << "host"
+ << "h3")))));
+
+ ASSERT_OK(oldConfig.validate());
+ ASSERT_OK(newConfig.validate());
+ ASSERT_OK(oldConfigRefresh.validate());
+ ASSERT_OK(
+ validateConfigForReconfig(&externalState, oldConfig, oldConfigRefresh, false).getStatus());
+ ASSERT_EQUALS(
+ ErrorCodes::NewReplicaSetConfigurationIncompatible,
+ validateConfigForReconfig(&externalState, oldConfig, newConfig, false).getStatus());
+
+ // Forced reconfigs also do not allow this.
+ ASSERT_EQUALS(
+ ErrorCodes::NewReplicaSetConfigurationIncompatible,
+ validateConfigForReconfig(&externalState, oldConfig, newConfig, true).getStatus());
+}
+
+TEST(ValidateConfigForReconfig, NewConfigMustNotFlipArbiterFlag) {
+ ReplicationCoordinatorExternalStateMock externalState;
+ externalState.addSelf(HostAndPort("h1"));
+
+ ReplicaSetConfig oldConfig;
+ ReplicaSetConfig newConfig;
+ ReplicaSetConfig oldConfigRefresh;
+
+ // Three configurations, two compatible except that h2 flips the arbiterOnly flag.
+ // The third, compatible with the first.
+ ASSERT_OK(oldConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2"
+ << "arbiterOnly" << false)
+ << BSON("_id" << 3 << "host"
+ << "h3")))));
+
+ ASSERT_OK(newConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 3 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2"
+ << "arbiterOnly" << true)
+ << BSON("_id" << 3 << "host"
+ << "h3")))));
+
+ ASSERT_OK(
+ oldConfigRefresh.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2"
+ << "arbiterOnly" << false)
+ << BSON("_id" << 3 << "host"
+ << "h3")))));
+
+ ASSERT_OK(oldConfig.validate());
+ ASSERT_OK(newConfig.validate());
+ ASSERT_OK(oldConfigRefresh.validate());
+ ASSERT_OK(
+ validateConfigForReconfig(&externalState, oldConfig, oldConfigRefresh, false).getStatus());
+ ASSERT_EQUALS(
+ ErrorCodes::NewReplicaSetConfigurationIncompatible,
+ validateConfigForReconfig(&externalState, oldConfig, newConfig, false).getStatus());
+ // Forced reconfigs also do not allow this.
+ ASSERT_EQUALS(
+ ErrorCodes::NewReplicaSetConfigurationIncompatible,
+ validateConfigForReconfig(&externalState, oldConfig, newConfig, true).getStatus());
+}
+
+TEST(ValidateConfigForReconfig, HostAndIdRemappingRestricted) {
+ // When reconfiguring a replica set, it is allowed to introduce (host, id) pairs
+ // absent from the old config only when the hosts and ids were both individually
+ // absent in the old config.
+
+ ReplicationCoordinatorExternalStateMock externalState;
+ externalState.addSelf(HostAndPort("h1"));
+
+ ReplicaSetConfig oldConfig;
+ ReplicaSetConfig legalNewConfigWithNewHostAndId;
+ ReplicaSetConfig illegalNewConfigReusingHost;
+ ReplicaSetConfig illegalNewConfigReusingId;
+
+ ASSERT_OK(
+ oldConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2") << BSON("_id" << 3 << "host"
+ << "h3")))));
+ ASSERT_OK(oldConfig.validate());
+
+ //
+ // Here, the new config is valid because we've replaced (2, "h2") with
+ // (4, "h4"), so neither the member _id or host name were reused.
+ //
+ ASSERT_OK(
+ legalNewConfigWithNewHostAndId.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 4 << "host"
+ << "h4")
+ << BSON("_id" << 3 << "host"
+ << "h3")))));
+ ASSERT_OK(legalNewConfigWithNewHostAndId.validate());
+ ASSERT_OK(validateConfigForReconfig(
+ &externalState, oldConfig, legalNewConfigWithNewHostAndId, false).getStatus());
+
+ //
+ // Here, the new config is invalid because we've reused host name "h2" with
+ // new _id 4.
+ //
+ ASSERT_OK(illegalNewConfigReusingHost.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 4 << "host"
+ << "h2")
+ << BSON("_id" << 3 << "host"
+ << "h3")))));
+ ASSERT_OK(illegalNewConfigReusingHost.validate());
+ ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible,
+ validateConfigForReconfig(
+ &externalState, oldConfig, illegalNewConfigReusingHost, false).getStatus());
+ // Forced reconfigs also do not allow this.
+ ASSERT_EQUALS(ErrorCodes::NewReplicaSetConfigurationIncompatible,
+ validateConfigForReconfig(
+ &externalState, oldConfig, illegalNewConfigReusingHost, true).getStatus());
+ //
+ // Here, the new config is valid, because all we've changed is the name of
+ // the host representing _id 2.
+ //
+ ASSERT_OK(illegalNewConfigReusingId.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h4")
+ << BSON("_id" << 3 << "host"
+ << "h3")))));
+ ASSERT_OK(illegalNewConfigReusingId.validate());
+ ASSERT_OK(validateConfigForReconfig(&externalState, oldConfig, illegalNewConfigReusingId, false)
+ .getStatus());
+}
+
+TEST(ValidateConfigForReconfig, MustFindSelf) {
+ // Old and new config are same except for version change; this is just testing that we can
+ // find ourself in the new config.
+ ReplicaSetConfig oldConfig;
+ ASSERT_OK(
+ oldConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2") << BSON("_id" << 3 << "host"
+ << "h3")))));
+
+ ReplicaSetConfig newConfig;
+ ASSERT_OK(
+ newConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2") << BSON("_id" << 3 << "host"
+ << "h3")))));
+ ReplicationCoordinatorExternalStateMock notPresentExternalState;
+ ReplicationCoordinatorExternalStateMock presentOnceExternalState;
+ presentOnceExternalState.addSelf(HostAndPort("h2"));
+ ReplicationCoordinatorExternalStateMock presentThriceExternalState;
+ presentThriceExternalState.addSelf(HostAndPort("h3"));
+ presentThriceExternalState.addSelf(HostAndPort("h2"));
+ presentThriceExternalState.addSelf(HostAndPort("h1"));
+
+ ASSERT_EQUALS(ErrorCodes::NodeNotFound,
+ validateConfigForReconfig(&notPresentExternalState, oldConfig, newConfig, false)
+ .getStatus());
+ ASSERT_EQUALS(ErrorCodes::DuplicateKey,
+ validateConfigForReconfig(
+ &presentThriceExternalState, oldConfig, newConfig, false).getStatus());
+ ASSERT_EQUALS(1,
+ unittest::assertGet(validateConfigForReconfig(
+ &presentOnceExternalState, oldConfig, newConfig, false)));
+ // Forced reconfigs also do not allow this.
+ ASSERT_EQUALS(ErrorCodes::NodeNotFound,
+ validateConfigForReconfig(&notPresentExternalState, oldConfig, newConfig, true)
+ .getStatus());
+ ASSERT_EQUALS(ErrorCodes::DuplicateKey,
+ validateConfigForReconfig(&presentThriceExternalState, oldConfig, newConfig, true)
+ .getStatus());
+ ASSERT_EQUALS(1,
+ unittest::assertGet(validateConfigForReconfig(
+ &presentOnceExternalState, oldConfig, newConfig, true)));
+}
+
+TEST(ValidateConfigForReconfig, SelfMustEndElectable) {
+ // Old and new config are same except for version change and the electability of one node;
+ // this is just testing that we must be electable in the new config.
+ ReplicaSetConfig oldConfig;
+ ASSERT_OK(
+ oldConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2") << BSON("_id" << 3 << "host"
+ << "h3")))));
+
+ ReplicaSetConfig newConfig;
+ ASSERT_OK(newConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h1")
+ << BSON("_id" << 2 << "host"
+ << "h2"
+ << "priority" << 0)
+ << BSON("_id" << 3 << "host"
+ << "h3")))));
+ ReplicationCoordinatorExternalStateMock presentOnceExternalState;
+ presentOnceExternalState.addSelf(HostAndPort("h2"));
+
+ ASSERT_EQUALS(ErrorCodes::NodeNotElectable,
+ validateConfigForReconfig(&presentOnceExternalState, oldConfig, newConfig, false)
+ .getStatus());
+ // Forced reconfig does not require electability.
+ ASSERT_OK(validateConfigForReconfig(&presentOnceExternalState, oldConfig, newConfig, true)
+ .getStatus());
+}
+
+TEST(ValidateConfigForInitiate, NewConfigInvalid) {
+ // The new config is not valid due to a duplicate _id value. This tests that if the new
+ // config is invalid, validateConfigForInitiate will return a status indicating what is
+ // wrong with the new config.
+ ReplicaSetConfig newConfig;
+ ASSERT_OK(newConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 0 << "host"
+ << "h2")
+ << BSON("_id" << 0 << "host"
+ << "h3")))));
+
+ ReplicationCoordinatorExternalStateMock presentOnceExternalState;
+ presentOnceExternalState.addSelf(HostAndPort("h2"));
+ ASSERT_EQUALS(ErrorCodes::BadValue,
+ validateConfigForInitiate(&presentOnceExternalState, newConfig).getStatus());
+}
+
+TEST(ValidateConfigForReconfig, NewConfigInvalid) {
+ // The new config is not valid due to a duplicate _id value. This tests that if the new
+ // config is invalid, validateConfigForReconfig will return a status indicating what is
+ // wrong with the new config.
+ ReplicaSetConfig oldConfig;
+ ASSERT_OK(oldConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 0 << "host"
+ << "h2")))));
+
+ ReplicaSetConfig newConfig;
+ ASSERT_OK(newConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 0 << "host"
+ << "h2")
+ << BSON("_id" << 0 << "host"
+ << "h3")))));
+
+ ReplicationCoordinatorExternalStateMock presentOnceExternalState;
+ presentOnceExternalState.addSelf(HostAndPort("h2"));
+ ASSERT_EQUALS(ErrorCodes::BadValue,
+ validateConfigForReconfig(&presentOnceExternalState, oldConfig, newConfig, false)
+ .getStatus());
+ // Forced reconfigs also do not allow this.
+ ASSERT_EQUALS(ErrorCodes::BadValue,
+ validateConfigForReconfig(&presentOnceExternalState, oldConfig, newConfig, true)
+ .getStatus());
+}
+
+TEST(ValidateConfigForStartUp, NewConfigInvalid) {
+ // The new config is not valid due to a duplicate _id value. This tests that if the new
+ // config is invalid, validateConfigForStartUp will return a status indicating what is wrong
+ // with the new config.
+ ReplicaSetConfig oldConfig;
+ ASSERT_OK(oldConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 0 << "host"
+ << "h2")))));
+
+ ReplicaSetConfig newConfig;
+ ASSERT_OK(newConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 0 << "host"
+ << "h2")
+ << BSON("_id" << 0 << "host"
+ << "h3")))));
+
+ ReplicationCoordinatorExternalStateMock presentOnceExternalState;
+ presentOnceExternalState.addSelf(HostAndPort("h2"));
+ ASSERT_EQUALS(
+ ErrorCodes::BadValue,
+ validateConfigForStartUp(&presentOnceExternalState, oldConfig, newConfig).getStatus());
+}
+
+TEST(ValidateConfigForStartUp, OldAndNewConfigIncompatible) {
+ // The new config is not compatible with the old config due to a member changing _ids. This
+ // tests that validateConfigForStartUp will return a status indicating the incompatiblilty
+ // between the old and new config.
+ ReplicaSetConfig oldConfig;
+ ASSERT_OK(oldConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 0 << "host"
+ << "h2")
+ << BSON("_id" << 1 << "host"
+ << "h3")))));
+
+
+ ReplicaSetConfig newConfig;
+ ASSERT_OK(newConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 2 << "host"
+ << "h2")
+ << BSON("_id" << 1 << "host"
+ << "h3")))));
+
+ ReplicationCoordinatorExternalStateMock presentOnceExternalState;
+ presentOnceExternalState.addSelf(HostAndPort("h2"));
+ ASSERT_EQUALS(
+ ErrorCodes::NewReplicaSetConfigurationIncompatible,
+ validateConfigForStartUp(&presentOnceExternalState, oldConfig, newConfig).getStatus());
+}
+
+TEST(ValidateConfigForStartUp, OldAndNewConfigCompatible) {
+ // The new config is compatible with the old config. This tests that
+ // validateConfigForStartUp will return a Status::OK() indicating the validity of this
+ // config change.
+ ReplicaSetConfig oldConfig;
+ ASSERT_OK(oldConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 0 << "host"
+ << "h2")
+ << BSON("_id" << 1 << "host"
+ << "h3")))));
+
+
+ ReplicaSetConfig newConfig;
+ ASSERT_OK(newConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 0 << "host"
+ << "h2"
+ << "priority" << 3)
+ << BSON("_id" << 1 << "host"
+ << "h3")))));
+
+ ReplicationCoordinatorExternalStateMock presentOnceExternalState;
+ presentOnceExternalState.addSelf(HostAndPort("h2"));
+ ASSERT_OK(
+ validateConfigForStartUp(&presentOnceExternalState, oldConfig, newConfig).getStatus());
+}
+
+TEST(ValidateConfigForHeartbeatReconfig, NewConfigInvalid) {
+ // The new config is not valid due to a duplicate _id value. This tests that if the new
+ // config is invalid, validateConfigForHeartbeatReconfig will return a status indicating
+ // what is wrong with the new config.
+ ReplicaSetConfig newConfig;
+ ASSERT_OK(newConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 0 << "host"
+ << "h2")
+ << BSON("_id" << 0 << "host"
+ << "h3")))));
+
+ ReplicationCoordinatorExternalStateMock presentOnceExternalState;
+ presentOnceExternalState.addSelf(HostAndPort("h2"));
+ ASSERT_EQUALS(
+ ErrorCodes::BadValue,
+ validateConfigForHeartbeatReconfig(&presentOnceExternalState, newConfig).getStatus());
+}
+
+TEST(ValidateConfigForHeartbeatReconfig, NewConfigValid) {
+ // The new config is valid. This tests that validateConfigForHeartbeatReconfig will return
+ // a Status::OK() indicating the validity of this config change.
+ ReplicaSetConfig newConfig;
+ ASSERT_OK(newConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 0 << "host"
+ << "h2")
+ << BSON("_id" << 1 << "host"
+ << "h3")))));
+
+ ReplicationCoordinatorExternalStateMock presentOnceExternalState;
+ presentOnceExternalState.addSelf(HostAndPort("h2"));
+ ASSERT_OK(validateConfigForHeartbeatReconfig(&presentOnceExternalState, newConfig).getStatus());
+}
+
+TEST(ValidateForReconfig, ForceStillNeedsValidConfig) {
+ // The new config is invalid due to two nodes with the same _id value. This tests that
+ // ValidateForReconfig fails with an invalid config, even if force is true.
+ ReplicaSetConfig oldConfig;
+ ASSERT_OK(oldConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 0 << "host"
+ << "h2")
+ << BSON("_id" << 1 << "host"
+ << "h3")))));
+
+
+ ReplicaSetConfig newConfig;
+ ASSERT_OK(newConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 0 << "host"
+ << "h2")
+ << BSON("_id" << 0 << "host"
+ << "h3")))));
+
+ ReplicationCoordinatorExternalStateMock presentOnceExternalState;
+ presentOnceExternalState.addSelf(HostAndPort("h2"));
+ ASSERT_EQUALS(ErrorCodes::BadValue,
+ validateConfigForReconfig(&presentOnceExternalState, oldConfig, newConfig, true)
+ .getStatus());
+}
+
+TEST(ValidateForReconfig, ForceStillNeedsSelfPresent) {
+ // The new config does not contain self. This tests that ValidateForReconfig fails
+ // if the member receiving it is absent from the config, even if force is true.
+ ReplicaSetConfig oldConfig;
+ ASSERT_OK(oldConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 1 << "members"
+ << BSON_ARRAY(BSON("_id" << 0 << "host"
+ << "h2")
+ << BSON("_id" << 1 << "host"
+ << "h3")))));
+
+
+ ReplicaSetConfig newConfig;
+ ASSERT_OK(newConfig.initialize(BSON("_id"
+ << "rs0"
+ << "version" << 2 << "members"
+ << BSON_ARRAY(BSON("_id" << 1 << "host"
+ << "h3")
+ << BSON("_id" << 2 << "host"
+ << "h4")))));
+
+ ReplicationCoordinatorExternalStateMock presentOnceExternalState;
+ presentOnceExternalState.addSelf(HostAndPort("h2"));
+ ASSERT_EQUALS(ErrorCodes::NodeNotFound,
+ validateConfigForReconfig(&presentOnceExternalState, oldConfig, newConfig, true)
+ .getStatus());
+}
} // namespace
} // namespace repl