0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00

SERVER-56241 Don't allow setting getLastErrorDefaults on startup/reconfig

This commit is contained in:
Huayu Ouyang 2021-04-29 22:27:50 +00:00 committed by Evergreen Agent
parent 0de67b8e46
commit f6b96a603d
9 changed files with 298 additions and 109 deletions

View File

@ -0,0 +1,139 @@
/**
* Tests that upgrading a set that has a non-default getLastErrorDefaults field to 'latest'
* will trigger an fassert on startup.
* TODO SERVER-56576: Remove upgrade_with_non_default_get_last_error_defaults_fails_on_startup.js
* once we branch 5.0
*/
(function() {
"use strict";
load("jstests/multiVersion/libs/multi_rs.js");
load('jstests/multiVersion/libs/multi_cluster.js');
// Checking UUID/index consistency and orphans involves talking to shards, but this test shuts down
// one shard.
TestData.skipCheckingUUIDsConsistentAcrossCluster = true;
TestData.skipCheckingIndexesConsistentAcrossCluster = true;
TestData.skipCheckOrphans = true;
function testReplSet(gleDefaults) {
const replTest = new ReplSetTest({
nodes: {
0: {binVersion: "last-lts"},
1: {binVersion: "last-lts"},
},
settings: gleDefaults
});
replTest.startSet();
replTest.initiate();
const primary = replTest.getPrimary();
// Test that the current config has the expected fields.
let config = primary.adminCommand({replSetGetConfig: 1}).config;
assert.eq(
config.settings.getLastErrorDefaults.w, gleDefaults.getLastErrorDefaults.w, tojson(config));
assert.eq(config.settings.getLastErrorDefaults.wTimeout,
gleDefaults.getLastErrorDefaults.wTimeout,
tojson(config));
assert.eq(
config.settings.getLastErrorDefaults.j, gleDefaults.getLastErrorDefaults.j, tojson(config));
assert.eq(config.settings.getLastErrorDefaults.fsync,
gleDefaults.getLastErrorDefaults.fsync,
tojson(config));
clearRawMongoProgramOutput();
jsTestLog("Attempting to upgrade set");
assert.soon(
function() {
try {
replTest.upgradeSet({binVersion: "latest"});
return false;
} catch (ex) {
return true;
}
},
"Node should fail when starting up with a non-default getLastErrorDefaults field",
ReplSetTest.kDefaultTimeoutMS);
assert(rawMongoProgramOutput().match('Fatal assertion.*5624100'),
'Node should fassert when starting up with a non-default getLastErrorDefaults field');
replTest.stopSet();
}
function testSharding(gleDefaults) {
const st = new ShardingTest({
shards: {
rs0: {
nodes: {
0: {binVersion: "last-lts"},
1: {binVersion: "last-lts"},
},
settings: gleDefaults
},
rs1: {
nodes: {
0: {binVersion: "last-lts"},
1: {binVersion: "last-lts"},
},
settings: gleDefaults
}
},
other: {mongosOptions: {binVersion: "last-lts"}, configOptions: {binVersion: "last-lts"}}
});
// Test that the current config has the expected fields.
const primary = st.rs0.getPrimary();
const config = primary.adminCommand({replSetGetConfig: 1}).config;
assert.eq(
config.settings.getLastErrorDefaults.w, gleDefaults.getLastErrorDefaults.w, tojson(config));
assert.eq(config.settings.getLastErrorDefaults.wTimeout,
gleDefaults.getLastErrorDefaults.wTimeout,
tojson(config));
assert.eq(
config.settings.getLastErrorDefaults.j, gleDefaults.getLastErrorDefaults.j, tojson(config));
assert.eq(config.settings.getLastErrorDefaults.fsync,
gleDefaults.getLastErrorDefaults.fsync,
tojson(config));
clearRawMongoProgramOutput();
jsTestLog("Attempting to upgrade set");
assert.soon(
function() {
try {
st.upgradeCluster(
'latest', {upgradeShards: true, upgradeConfigs: false, upgradeMongos: false});
return false;
} catch (ex) {
return true;
}
},
"Node should fail when starting up with a non-default getLastErrorDefaults field",
ReplSetTest.kDefaultTimeoutMS);
assert(rawMongoProgramOutput().match('Fatal assertion.*5624100'),
'Node should fassert when starting up with a non-default getLastErrorDefaults field');
st.stop();
}
function runTest(gleDefaults) {
testReplSet(gleDefaults);
testSharding(gleDefaults);
}
jsTestLog("Testing getLastErrorDefaults with {w: 'majority'}");
runTest({getLastErrorDefaults: {w: 'majority', wtimeout: 0}});
jsTestLog("Testing getLastErrorDefaults with {w:1, wtimeout: 1}");
runTest({getLastErrorDefaults: {w: 1, wtimeout: 1}});
jsTestLog("Testing getLastErrorDefaults with {w:1, wtimeout: 0, j: true}");
runTest({getLastErrorDefaults: {w: 1, wtimeout: 0, j: true}});
jsTestLog("Testing getLastErrorDefaults with {w:1, wtimeout: 0, fsync: true}");
runTest({getLastErrorDefaults: {w: 1, wtimeout: 0, fsync: true}});
jsTestLog("Testing getLastErrorDefaults with {w:1, wtimeout: 0, j: false}");
runTest({getLastErrorDefaults: {w: 1, wtimeout: 0, j: false}});
}());

View File

@ -0,0 +1,73 @@
/*
* Test that initiating and reconfiguring when settings.getLastErrorDefaults is set
* and not {w:1, wtimeout: 0} will fail.
* @tags: [requires_fcv_50]
*/
(function() {
"use strict";
function testInitiate(gleDefaults) {
const replTest = new ReplSetTest({name: jsTestName(), nodes: 1});
clearRawMongoProgramOutput();
const conns = replTest.startSet();
const admin = conns[0].getDB("admin");
const conf = replTest.getReplSetConfig();
conf.settings = gleDefaults;
assert.soon(
function() {
try {
admin.runCommand({replSetInitiate: conf});
return false;
} catch (ex) {
return true;
}
},
"Node should fail when initiating with a non-default getLastErrorDefaults field",
ReplSetTest.kDefaultTimeoutMS);
assert(
rawMongoProgramOutput().match('Fatal assertion.*5624101'),
"Node should have fasserted when initiating with a non-default getLastErrorDefaults field");
replTest.stop(conns[0], undefined, {allowedExitCode: MongoRunner.EXIT_ABRUPT});
replTest.stopSet(undefined, undefined, {skipValidation: true});
}
function testReconfig(gleDefaults) {
const replTest = new ReplSetTest({name: jsTestName(), nodes: 1});
const conns = replTest.startSet();
const admin = conns[0].getDB("admin");
replTest.initiate();
const conf = admin.runCommand({replSetGetConfig: 1}).config;
conf.settings = gleDefaults;
conf.version++;
jsTestLog("Node should fail to reconfig with a non-default getLastErrorDefaults field.");
assert.commandFailedWithCode(admin.runCommand({replSetReconfig: conf}), 5624102);
assert.commandFailedWithCode(admin.runCommand({replSetReconfig: conf, force: true}), 5624102);
replTest.stopSet();
}
function runTest(gleDefaults) {
testInitiate(gleDefaults);
testReconfig(gleDefaults);
}
jsTestLog("Testing getLastErrorDefaults with {w: 'majority'}");
runTest({getLastErrorDefaults: {w: 'majority', wtimeout: 0}});
jsTestLog("Testing getLastErrorDefaults with {w:1, wtimeout: 1}");
runTest({getLastErrorDefaults: {w: 1, wtimeout: 1}});
jsTestLog("Testing getLastErrorDefaults with {w:1, wtimeout: 0, j: true}");
runTest({getLastErrorDefaults: {w: 1, wtimeout: 0, j: true}});
jsTestLog("Testing getLastErrorDefaults with {w:1, wtimeout: 0, fsync: true}");
runTest({getLastErrorDefaults: {w: 1, wtimeout: 0, fsync: true}});
jsTestLog("Testing getLastErrorDefaults with {w:1, wtimeout: 0, j: false}");
runTest({getLastErrorDefaults: {w: 1, wtimeout: 0, j: false}});
}());

View File

@ -382,17 +382,6 @@ Status ReplSetConfig::_validate(bool allowSplitHorizonIP) const {
"one non-arbiter member with priority > 0");
}
// This validation must be done outside the IDL because we need to parse the settings object
// completely to get the custom write modes.
const auto& defaultWriteConcern = getSettings()->getDefaultWriteConcern();
if (!defaultWriteConcern.wMode.empty() &&
WriteConcernOptions::kMajority != defaultWriteConcern.wMode &&
!findCustomWriteMode(defaultWriteConcern.wMode).isOK()) {
return Status(ErrorCodes::BadValue,
str::stream() << "Default write concern requires undefined write mode "
<< defaultWriteConcern.wMode);
}
if (getConfigServer()) {
if (arbiterCount > 0) {
return Status(ErrorCodes::BadValue,
@ -728,6 +717,14 @@ bool ReplSetConfig::isImplicitDefaultWriteConcernMajority() const {
return arbiters == 0 || _writableVotingMembersCount > _majorityVoteCount;
}
bool ReplSetConfig::containsCustomizedGetLastErrorDefaults() const {
// Since the ReplSetConfig always has a WriteConcernOptions, the only way to know if it has been
// customized through getLastErrorDefaults is if it's different from { w: 1, wtimeout: 0 }.
const auto& getLastErrorDefaults = getDefaultWriteConcern();
return !(getLastErrorDefaults.wNumNodes == 1 && getLastErrorDefaults.wTimeout == 0 &&
getLastErrorDefaults.syncMode == WriteConcernOptions::SyncMode::UNSET);
}
MemberConfig* MutableReplSetConfig::_findMemberByID(MemberId id) {
for (auto it = getMembers().begin(); it != getMembers().end(); ++it) {
if (it->getId() == id) {

View File

@ -536,6 +536,11 @@ public:
return getNumMembers() == 3 && getNumDataBearingMembers() == 2;
}
/**
* Returns true if the getLastErrorDefaults has been customized.
*/
bool containsCustomizedGetLastErrorDefaults() const;
private:
/**
* Sets replica set ID to 'defaultReplicaSetId' if 'cfg' does not contain an ID.

View File

@ -431,6 +431,15 @@ StatusWith<int> validateConfigForStartUp(ReplicationCoordinatorExternalState* ex
if (!status.isOK()) {
return StatusWith<int>(status);
}
if (newConfig.containsCustomizedGetLastErrorDefaults()) {
fassertFailedWithStatusNoTrace(
5624100,
{ErrorCodes::IllegalOperation,
str::stream() << "Failed to start up: Replica set config contains customized "
"getLastErrorDefaults, which "
"has been deprecated and is now ignored. Use setDefaultRWConcern "
"instead to set a cluster-wide default writeConcern."});
}
return findSelfInConfig(externalState, newConfig, ctx);
}
@ -447,10 +456,14 @@ StatusWith<int> validateConfigForInitiate(ReplicationCoordinatorExternalState* e
return StatusWith<int>(status);
}
status = newConfig.checkIfWriteConcernCanBeSatisfied(newConfig.getDefaultWriteConcern());
if (!status.isOK()) {
return status.withContext(
"Found invalid default write concern in 'getLastErrorDefaults' field");
if (newConfig.containsCustomizedGetLastErrorDefaults()) {
fassertFailedWithStatusNoTrace(
5624101,
{ErrorCodes::IllegalOperation,
str::stream() << "Failed to initiate: Replica set config contains customized "
"getLastErrorDefaults, which "
"has been deprecated and is now ignored. Use setDefaultRWConcern "
"instead to set a cluster-wide default writeConcern."});
}
status = validateArbiterPriorities(newConfig);
@ -496,11 +509,12 @@ Status validateConfigForReconfig(const ReplSetConfig& oldConfig,
}
}
status = newConfig.checkIfWriteConcernCanBeSatisfied(newConfig.getDefaultWriteConcern());
if (!status.isOK()) {
return status.withContext(
"Found invalid default write concern in 'getLastErrorDefaults' field");
}
uassert(5624102,
"Failed to reconfig: Replica set config contains customized "
"getLastErrorDefaults, which has "
"been deprecated and is now ignored. Use setDefaultRWConcern instead to "
"set a cluster-wide default writeConcern.",
!newConfig.containsCustomizedGetLastErrorDefaults());
status = validateOldAndNewConfigsCompatible(oldConfig, newConfig);
if (!status.isOK()) {
@ -534,6 +548,13 @@ StatusWith<int> validateConfigForHeartbeatReconfig(
return StatusWith<int>(status);
}
tassert(5624103,
"Replica set config during heartbeat reconfig contains "
"customized getLastErrorDefaults, which has "
"been deprecated and is now ignored. Use setDefaultRWConcern instead to "
"set a cluster-wide default writeConcern.",
!newConfig.containsCustomizedGetLastErrorDefaults());
return findSelfInConfig(externalState, newConfig, ctx);
}

View File

@ -40,6 +40,7 @@
#include "mongo/db/server_options.h"
#include "mongo/db/service_context.h"
#include "mongo/db/service_context_test_fixture.h"
#include "mongo/unittest/death_test.h"
#include "mongo/unittest/ensure_fcv.h"
#include "mongo/unittest/unittest.h"
@ -155,7 +156,9 @@ TEST_F(ServiceContextTest, ValidateConfigForInitiate_SelfMustBeElectable) {
.getStatus());
}
TEST_F(ServiceContextTest, ValidateConfigForInitiate_WriteConcernMustBeSatisfiable) {
DEATH_TEST_REGEX_F(ServiceContextTest,
ValidateConfigForInitiate_NonDefaultGetLastErrorDefaults,
"Fatal assertion.*5624101") {
ReplSetConfig config;
OID newReplSetId = OID::gen();
config =
@ -170,10 +173,7 @@ TEST_F(ServiceContextTest, ValidateConfigForInitiate_WriteConcernMustBeSatisfiab
newReplSetId);
ReplicationCoordinatorExternalStateMock presentOnceExternalState;
presentOnceExternalState.addSelf(HostAndPort("h2"));
ASSERT_EQUALS(ErrorCodes::UnsatisfiableWriteConcern,
validateConfigForInitiate(&presentOnceExternalState, config, getServiceContext())
.getStatus());
validateConfigForInitiate(&presentOnceExternalState, config, getServiceContext()).getStatus();
}
TEST_F(ServiceContextTest, ValidateConfigForInitiate_ArbiterPriorityMustBeZeroOrOne) {
@ -771,7 +771,7 @@ TEST_F(ServiceContextTest, ValidateConfigForReconfig_NewConfigInvalid) {
validateConfigForReconfig(oldConfig, newConfig, true, false, false));
}
TEST_F(ServiceContextTest, ValidateConfigForReconfig_NewConfigWriteConcernNotSatisifiable) {
TEST_F(ServiceContextTest, ValidateConfigForReconfig_NonDefaultGetLastErrorDefaults) {
// The new config is not valid due to an unsatisfiable write concern. This tests that if the
// new config is invalid, validateConfigForReconfig will return a status indicating what is
// wrong with the new config.
@ -793,11 +793,13 @@ TEST_F(ServiceContextTest, ValidateConfigForReconfig_NewConfigWriteConcernNotSat
ReplicationCoordinatorExternalStateMock presentOnceExternalState;
presentOnceExternalState.addSelf(HostAndPort("h2"));
ASSERT_EQUALS(ErrorCodes::UnsatisfiableWriteConcern,
validateConfigForReconfig(oldConfig, newConfig, false, false, false));
ASSERT_THROWS_CODE(validateConfigForReconfig(oldConfig, newConfig, false, false, false),
AssertionException,
5624102);
// Forced reconfigs also do not allow this.
ASSERT_EQUALS(ErrorCodes::UnsatisfiableWriteConcern,
validateConfigForReconfig(oldConfig, newConfig, true, false, false));
ASSERT_THROWS_CODE(validateConfigForReconfig(oldConfig, newConfig, true, false, false),
AssertionException,
5624102);
}
TEST_F(ServiceContextTest, ValidateConfigForStartUp_NewConfigInvalid) {
@ -840,7 +842,9 @@ TEST_F(ServiceContextTest, ValidateConfigForStartUp_NewConfigValid) {
.getStatus());
}
TEST_F(ServiceContextTest, ValidateConfigForStartUp_NewConfigWriteConcernNotSatisfiable) {
DEATH_TEST_REGEX_F(ServiceContextTest,
ValidateConfigForStartUp_NewConfigNonDefaultGetLastErrorDefaults,
"Fatal assertion.*5624100") {
// The new config contains an unsatisfiable write concern. We don't allow these configs to be
// created anymore, but we allow any which exist to pass and the database to start up to
// maintain backwards compatibility.
@ -855,8 +859,7 @@ TEST_F(ServiceContextTest, ValidateConfigForStartUp_NewConfigWriteConcernNotSati
ReplicationCoordinatorExternalStateMock presentOnceExternalState;
presentOnceExternalState.addSelf(HostAndPort("h2"));
ASSERT_OK(validateConfigForStartUp(&presentOnceExternalState, newConfig, getServiceContext())
.getStatus());
validateConfigForStartUp(&presentOnceExternalState, newConfig, getServiceContext()).getStatus();
}
TEST_F(ServiceContextTest, ValidateConfigForHeartbeatReconfig_NewConfigInvalid) {
@ -899,7 +902,9 @@ TEST_F(ServiceContextTest, ValidateConfigForHeartbeatReconfig_NewConfigValid) {
.getStatus());
}
TEST_F(ServiceContextTest, ValidateConfigForHeartbeatReconfig_NewConfigWriteConcernNotSatisfiable) {
DEATH_TEST_REGEX_F(ServiceContextTest,
ValidateConfigForHeartbeatReconfig_NonDefaultGetLastErrorDefaults,
"Tripwire assertion.*5624103") {
// The new config contains an unsatisfiable write concern. We don't allow these configs to be
// created anymore, but we allow any which exist to be received in a heartbeat.
ReplSetConfig newConfig;
@ -915,9 +920,11 @@ TEST_F(ServiceContextTest, ValidateConfigForHeartbeatReconfig_NewConfigWriteConc
ReplicationCoordinatorExternalStateMock presentOnceExternalState;
presentOnceExternalState.addSelf(HostAndPort("h2"));
ASSERT_OK(validateConfigForHeartbeatReconfig(
&presentOnceExternalState, newConfig, getServiceContext())
.getStatus());
ASSERT_THROWS_CODE(validateConfigForHeartbeatReconfig(
&presentOnceExternalState, newConfig, getServiceContext())
.getStatus(),
AssertionException,
5624103);
}
TEST_F(ServiceContextTest, ValidateForReconfig_ForceStillNeedsValidConfig) {

View File

@ -118,10 +118,7 @@ TEST(ReplSetConfig, ParseLargeConfigAndCheckAccessors) {
<< BSON("NYC"
<< "NY")))
<< "protocolVersion" << 1 << "settings"
<< BSON("getLastErrorDefaults"
<< BSON("w"
<< "majority")
<< "getLastErrorModes"
<< BSON("getLastErrorModes"
<< BSON("eastCoast" << BSON("NYC" << 1))
<< "chainingAllowed" << false << "heartbeatIntervalMillis"
<< 5000 << "heartbeatTimeoutSecs" << 120
@ -132,8 +129,6 @@ TEST(ReplSetConfig, ParseLargeConfigAndCheckAccessors) {
ASSERT_EQUALS(1, config.getConfigTerm());
ASSERT_EQUALS(1, config.getNumMembers());
ASSERT_EQUALS(MemberId(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.getConfigServer());
@ -1230,59 +1225,6 @@ TEST(ReplSetConfig, HeartbeatTimeoutField) {
DBException);
}
TEST(ReplSetConfig, GleDefaultField) {
ReplSetConfig config(
ReplSetConfig::parse(BSON("_id"
<< "rs0"
<< "version" << 1 << "protocolVersion" << 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);
config = ReplSetConfig::parse(BSON("_id"
<< "rs0"
<< "version" << 1 << "protocolVersion" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"))
<< "settings"
<< BSON("getLastErrorDefaults" << BSON("w"
<< "frim"))));
ASSERT_EQUALS(ErrorCodes::BadValue, config.validate());
// Test that default write concern must have at least one member.
ASSERT_THROWS(
ReplSetConfig::parse(BSON("_id"
<< "rs0"
<< "version" << 1 << "protocolVersion" << 1 << "members"
<< BSON_ARRAY(BSON("_id" << 0 << "host"
<< "localhost:12345"))
<< "settings" << BSON("getLastErrorDefaults" << BSON("w" << 0)))),
DBException);
config = ReplSetConfig::parse(BSON("_id"
<< "rs0"
<< "version" << 1 << "protocolVersion" << 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) {
@ -1416,14 +1358,11 @@ TEST(ReplSetConfig, toBSONRoundTripAbilityLarge) {
<< "true")))
<< "protocolVersion" << 1 << "settings"
<< BSON("heartbeatIntervalMillis" << 5000 << "heartbeatTimeoutSecs" << 20
<< "electionTimeoutMillis" << 4 << "chainingAllowed"
<< true << "getLastErrorDefaults"
<< BSON("w"
<< "majority")
<< "getLastErrorModes"
<< BSON("disks" << BSON("ssd" << 1 << "hdd" << 1)
<< "coasts" << BSON("coast" << 2)))));
<< BSON("heartbeatIntervalMillis"
<< 5000 << "heartbeatTimeoutSecs" << 20 << "electionTimeoutMillis" << 4
<< "chainingAllowed" << true << "getLastErrorModes"
<< BSON("disks" << BSON("ssd" << 1 << "hdd" << 1) << "coasts"
<< BSON("coast" << 2)))));
BSONObj configObjA = configA.toBSON();
configB = ReplSetConfig::parse(configObjA);
ASSERT_TRUE(configA == configB);

View File

@ -4563,17 +4563,17 @@ ReplicationCoordinatorImpl::_setCurrentRSConfig(WithLock lk,
}
}
// Since the ReplSetConfig always has a WriteConcernOptions, the only way to know if it has been
// customized is if it's different to the implicit defaults of { w: 1, wtimeout: 0 }.
if (const auto& wc = newConfig.getDefaultWriteConcern();
!(wc.wNumNodes == 1 && wc.wTimeout == 0)) {
// Check that getLastErrorDefaults has not been changed from the default settings of
// { w: 1, wtimeout: 0 }.
if (newConfig.containsCustomizedGetLastErrorDefaults()) {
LOGV2_OPTIONS(21387, {logv2::LogTag::kStartupWarnings}, "");
LOGV2_OPTIONS(21388,
{logv2::LogTag::kStartupWarnings},
"** WARNING: Replica set config contains customized getLastErrorDefaults,");
LOGV2_OPTIONS(21389,
{logv2::LogTag::kStartupWarnings},
"** which are deprecated. Use setDefaultRWConcern instead to set a");
"** which have been deprecated and are now ignored. Use "
"setDefaultRWConcern instead to set a");
LOGV2_OPTIONS(21390,
{logv2::LogTag::kStartupWarnings},
"** cluster-wide default writeConcern.");

View File

@ -37,6 +37,7 @@
#include "mongo/db/auth/privilege.h"
#include "mongo/db/commands.h"
#include "mongo/db/dbdirectclient.h"
#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/db/s/add_shard_cmd_gen.h"
#include "mongo/db/s/add_shard_util.h"
#include "mongo/rpc/get_status_from_command_result.h"
@ -60,6 +61,13 @@ public:
uassert(50876,
"Cannot run addShard on a node started without --shardsvr",
serverGlobalParams.clusterRole == ClusterRole::ShardServer);
tassert(5624104,
"Cannot run addShard on a node that contains customized getLastErrorDefaults, "
"which has been deprecated and is now ignored. Use setDefaultRWConcern instead "
"to set a cluster-wide default writeConcern.",
!repl::ReplicationCoordinator::get(opCtx)
->getConfig()
.containsCustomizedGetLastErrorDefaults());
auto addShardCmd = request();
auto shardIdUpsertCmd =