1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kReplication
32 
33 #include "mongo/platform/basic.h"
34 
35 #include "mongo/db/repl/is_master_response.h"
36 
37 #include "mongo/base/status.h"
38 #include "mongo/bson/oid.h"
39 #include "mongo/bson/util/bson_extract.h"
40 #include "mongo/db/jsobj.h"
41 #include "mongo/util/mongoutils/str.h"
42 
43 namespace mongo {
44 namespace repl {
45 namespace {
46 
47 const std::string kIsMasterFieldName = "ismaster";
48 const std::string kSecondaryFieldName = "secondary";
49 const std::string kSetNameFieldName = "setName";
50 const std::string kSetVersionFieldName = "setVersion";
51 const std::string kHostsFieldName = "hosts";
52 const std::string kPassivesFieldName = "passives";
53 const std::string kArbitersFieldName = "arbiters";
54 const std::string kPrimaryFieldName = "primary";
55 const std::string kArbiterOnlyFieldName = "arbiterOnly";
56 const std::string kPassiveFieldName = "passive";
57 const std::string kHiddenFieldName = "hidden";
58 const std::string kBuildIndexesFieldName = "buildIndexes";
59 const std::string kSlaveDelayFieldName = "slaveDelay";
60 const std::string kTagsFieldName = "tags";
61 const std::string kMeFieldName = "me";
62 const std::string kElectionIdFieldName = "electionId";
63 const std::string kLastWriteOpTimeFieldName = "opTime";
64 const std::string kLastWriteDateFieldName = "lastWriteDate";
65 const std::string kLastMajorityWriteOpTimeFieldName = "majorityOpTime";
66 const std::string kLastMajorityWriteDateFieldName = "majorityWriteDate";
67 const std::string kLastWriteFieldName = "lastWrite";
68 const std::string kIsWritablePrimaryFieldName = "isWritablePrimary";
69 const std::string kSecondaryDelaySecsFieldName = "secondaryDelaySecs";
70 
71 // field name constants that don't directly correspond to member variables
72 const std::string kInfoFieldName = "info";
73 const std::string kIsReplicaSetFieldName = "isreplicaset";
74 const std::string kErrmsgFieldName = "errmsg";
75 const std::string kCodeFieldName = "code";
76 
77 }  // namespace
78 
IsMasterResponse()79 IsMasterResponse::IsMasterResponse()
80     : _isMaster(false),
81       _isMasterSet(false),
82       _secondary(false),
83       _isSecondarySet(false),
84       _setNameSet(false),
85       _setVersion(0),
86       _setVersionSet(false),
87       _hostsSet(false),
88       _passivesSet(false),
89       _arbitersSet(false),
90       _primarySet(false),
91       _arbiterOnly(false),
92       _arbiterOnlySet(false),
93       _passive(false),
94       _passiveSet(false),
95       _hidden(false),
96       _hiddenSet(false),
97       _buildIndexes(true),
98       _buildIndexesSet(false),
99       _slaveDelay(0),
100       _slaveDelaySet(false),
101       _tagsSet(false),
102       _meSet(false),
103       _electionId(OID()),
104       _configSet(true),
105       _shutdownInProgress(false) {}
106 
addToBSON(BSONObjBuilder * builder,bool useLegacyResponseFields) const107 void IsMasterResponse::addToBSON(BSONObjBuilder* builder, bool useLegacyResponseFields) const {
108     if (_hostsSet) {
109         std::vector<std::string> hosts;
110         for (size_t i = 0; i < _hosts.size(); ++i) {
111             hosts.push_back(_hosts[i].toString());
112         }
113         builder->append(kHostsFieldName, hosts);
114     }
115     if (_passivesSet) {
116         std::vector<std::string> passives;
117         for (size_t i = 0; i < _passives.size(); ++i) {
118             passives.push_back(_passives[i].toString());
119         }
120         builder->append(kPassivesFieldName, passives);
121     }
122     if (_arbitersSet) {
123         std::vector<std::string> arbiters;
124         for (size_t i = 0; i < _arbiters.size(); ++i) {
125             arbiters.push_back(_arbiters[i].toString());
126         }
127         builder->append(kArbitersFieldName, arbiters);
128     }
129 
130     if (_setNameSet) {
131         builder->append(kSetNameFieldName, _setName);
132     }
133 
134     if (_shutdownInProgress) {
135         builder->append(kCodeFieldName, ErrorCodes::ShutdownInProgress);
136         builder->append(kErrmsgFieldName, "replication shutdown in progress");
137         return;
138     }
139 
140     if (!_configSet) {
141         if (useLegacyResponseFields) {
142             builder->append(kIsMasterFieldName, false);
143         } else {
144             builder->append(kIsWritablePrimaryFieldName, false);
145         }
146         builder->append(kSecondaryFieldName, false);
147         builder->append(kInfoFieldName, "Does not have a valid replica set config");
148         builder->append(kIsReplicaSetFieldName, true);
149         return;
150     }
151 
152     invariant(_setVersionSet);
153     builder->append(kSetVersionFieldName, static_cast<int>(_setVersion));
154     invariant(_isMasterSet);
155     if (useLegacyResponseFields) {
156         builder->append(kIsMasterFieldName, _isMaster);
157     } else {
158         builder->append(kIsWritablePrimaryFieldName, _isMaster);
159     }
160     invariant(_isSecondarySet);
161     builder->append(kSecondaryFieldName, _secondary);
162 
163     if (_primarySet)
164         builder->append(kPrimaryFieldName, _primary.toString());
165     if (_arbiterOnlySet)
166         builder->append(kArbiterOnlyFieldName, _arbiterOnly);
167     if (_passiveSet)
168         builder->append(kPassiveFieldName, _passive);
169     if (_hiddenSet)
170         builder->append(kHiddenFieldName, _hidden);
171     if (_buildIndexesSet)
172         builder->append(kBuildIndexesFieldName, _buildIndexes);
173     if (_slaveDelaySet) {
174         if (useLegacyResponseFields) {
175             builder->appendIntOrLL(kSlaveDelayFieldName, durationCount<Seconds>(_slaveDelay));
176         } else {
177             builder->appendIntOrLL(kSecondaryDelaySecsFieldName,
178                                    durationCount<Seconds>(_slaveDelay));
179         }
180     }
181     if (_tagsSet) {
182         BSONObjBuilder tags(builder->subobjStart(kTagsFieldName));
183         for (unordered_map<std::string, std::string>::const_iterator it = _tags.begin();
184              it != _tags.end();
185              ++it) {
186             tags.append(it->first, it->second);
187         }
188     }
189     invariant(_meSet);
190     builder->append(kMeFieldName, _me.toString());
191     if (_electionId.isSet())
192         builder->append(kElectionIdFieldName, _electionId);
193     if (_lastWrite || _lastMajorityWrite) {
194         BSONObjBuilder lastWrite(builder->subobjStart(kLastWriteFieldName));
195         if (_lastWrite) {
196             lastWrite.append(kLastWriteOpTimeFieldName, _lastWrite->opTime.toBSON());
197             lastWrite.appendTimeT(kLastWriteDateFieldName, _lastWrite->value);
198         }
199         if (_lastMajorityWrite) {
200             lastWrite.append(kLastMajorityWriteOpTimeFieldName,
201                              _lastMajorityWrite->opTime.toBSON());
202             lastWrite.appendTimeT(kLastMajorityWriteDateFieldName, _lastMajorityWrite->value);
203         }
204     }
205 }
206 
toBSON(bool useLegacyResponseFields) const207 BSONObj IsMasterResponse::toBSON(bool useLegacyResponseFields) const {
208     BSONObjBuilder builder;
209     addToBSON(&builder, useLegacyResponseFields);
210     return builder.obj();
211 }
212 
initialize(const BSONObj & doc)213 Status IsMasterResponse::initialize(const BSONObj& doc) {
214     Status status = bsonExtractBooleanField(doc, kIsMasterFieldName, &_isMaster);
215     if (!status.isOK()) {
216         return status;
217     }
218     _isMasterSet = true;
219     status = bsonExtractBooleanField(doc, kSecondaryFieldName, &_secondary);
220     if (!status.isOK()) {
221         return status;
222     }
223     _isSecondarySet = true;
224     if (doc.hasField(kInfoFieldName)) {
225         if (_isMaster || _secondary || !doc.hasField(kIsReplicaSetFieldName) ||
226             !doc[kIsReplicaSetFieldName].booleanSafe()) {
227             return Status(ErrorCodes::FailedToParse,
228                           str::stream() << "Expected presence of \"" << kInfoFieldName
229                                         << "\" field to indicate no valid config loaded, but other "
230                                            "fields weren't as we expected");
231         }
232         _configSet = false;
233         return Status::OK();
234     } else {
235         if (doc.hasField(kIsReplicaSetFieldName)) {
236             return Status(ErrorCodes::FailedToParse,
237                           str::stream() << "Found \"" << kIsReplicaSetFieldName
238                                         << "\" field which should indicate that no valid config "
239                                            "is loaded, but we didn't also have an \""
240                                         << kInfoFieldName
241                                         << "\" field as we expected");
242         }
243     }
244 
245     status = bsonExtractStringField(doc, kSetNameFieldName, &_setName);
246     if (!status.isOK()) {
247         return status;
248     }
249     _setNameSet = true;
250     status = bsonExtractIntegerField(doc, kSetVersionFieldName, &_setVersion);
251     if (!status.isOK()) {
252         return status;
253     }
254     _setVersionSet = true;
255 
256     if (doc.hasField(kHostsFieldName)) {
257         BSONElement hostsElement;
258         status = bsonExtractTypedField(doc, kHostsFieldName, Array, &hostsElement);
259         if (!status.isOK()) {
260             return status;
261         }
262         for (BSONObjIterator it(hostsElement.Obj()); it.more();) {
263             BSONElement hostElement = it.next();
264             if (hostElement.type() != String) {
265                 return Status(ErrorCodes::TypeMismatch,
266                               str::stream() << "Elements in \"" << kHostsFieldName
267                                             << "\" array of isMaster response must be of type "
268                                             << typeName(String)
269                                             << " but found type "
270                                             << typeName(hostElement.type()));
271             }
272             _hosts.push_back(HostAndPort(hostElement.String()));
273         }
274         _hostsSet = true;
275     }
276 
277     if (doc.hasField(kPassivesFieldName)) {
278         BSONElement passivesElement;
279         status = bsonExtractTypedField(doc, kPassivesFieldName, Array, &passivesElement);
280         if (!status.isOK()) {
281             return status;
282         }
283         for (BSONObjIterator it(passivesElement.Obj()); it.more();) {
284             BSONElement passiveElement = it.next();
285             if (passiveElement.type() != String) {
286                 return Status(ErrorCodes::TypeMismatch,
287                               str::stream() << "Elements in \"" << kPassivesFieldName
288                                             << "\" array of isMaster response must be of type "
289                                             << typeName(String)
290                                             << " but found type "
291                                             << typeName(passiveElement.type()));
292             }
293             _passives.push_back(HostAndPort(passiveElement.String()));
294         }
295         _passivesSet = true;
296     }
297 
298     if (doc.hasField(kArbitersFieldName)) {
299         BSONElement arbitersElement;
300         status = bsonExtractTypedField(doc, kArbitersFieldName, Array, &arbitersElement);
301         if (!status.isOK()) {
302             return status;
303         }
304         for (BSONObjIterator it(arbitersElement.Obj()); it.more();) {
305             BSONElement arbiterElement = it.next();
306             if (arbiterElement.type() != String) {
307                 return Status(ErrorCodes::TypeMismatch,
308                               str::stream() << "Elements in \"" << kArbitersFieldName
309                                             << "\" array of isMaster response must be of type "
310                                             << typeName(String)
311                                             << " but found type "
312                                             << typeName(arbiterElement.type()));
313             }
314             _arbiters.push_back(HostAndPort(arbiterElement.String()));
315         }
316         _arbitersSet = true;
317     }
318 
319     if (doc.hasField(kPrimaryFieldName)) {
320         std::string primaryString;
321         status = bsonExtractStringField(doc, kPrimaryFieldName, &primaryString);
322         if (!status.isOK()) {
323             return status;
324         }
325         _primary = HostAndPort(primaryString);
326         _primarySet = true;
327     }
328 
329     if (doc.hasField(kArbiterOnlyFieldName)) {
330         status = bsonExtractBooleanField(doc, kArbiterOnlyFieldName, &_arbiterOnly);
331         if (!status.isOK()) {
332             return status;
333         }
334         _arbiterOnlySet = true;
335     }
336 
337     if (doc.hasField(kPassiveFieldName)) {
338         status = bsonExtractBooleanField(doc, kPassiveFieldName, &_passive);
339         if (!status.isOK()) {
340             return status;
341         }
342         _passiveSet = true;
343     }
344 
345     if (doc.hasField(kHiddenFieldName)) {
346         status = bsonExtractBooleanField(doc, kHiddenFieldName, &_hidden);
347         if (!status.isOK()) {
348             return status;
349         }
350         _hiddenSet = true;
351     }
352 
353     if (doc.hasField(kBuildIndexesFieldName)) {
354         status = bsonExtractBooleanField(doc, kBuildIndexesFieldName, &_buildIndexes);
355         if (!status.isOK()) {
356             return status;
357         }
358         _buildIndexesSet = true;
359     }
360 
361     if (doc.hasField(kSlaveDelayFieldName)) {
362         long long slaveDelaySecs;
363         status = bsonExtractIntegerField(doc, kSlaveDelayFieldName, &slaveDelaySecs);
364         if (!status.isOK()) {
365             return status;
366         }
367         _slaveDelaySet = true;
368         _slaveDelay = Seconds(slaveDelaySecs);
369     }
370 
371     if (doc.hasField(kTagsFieldName)) {
372         BSONElement tagsElement;
373         status = bsonExtractTypedField(doc, kTagsFieldName, Object, &tagsElement);
374         if (!status.isOK()) {
375             return status;
376         }
377         for (BSONObjIterator it(tagsElement.Obj()); it.more();) {
378             BSONElement tagElement = it.next();
379             if (tagElement.type() != String) {
380                 return Status(ErrorCodes::TypeMismatch,
381                               str::stream() << "Elements in \"" << kTagsFieldName
382                                             << "\" obj "
383                                                "of isMaster response must be of type "
384                                             << typeName(String)
385                                             << " but found type "
386                                             << typeName(tagsElement.type()));
387             }
388             _tags[tagElement.fieldNameStringData().toString()] = tagElement.String();
389         }
390         _tagsSet = true;
391     }
392 
393     if (doc.hasField(kElectionIdFieldName)) {
394         BSONElement electionIdElem;
395         status = bsonExtractTypedField(doc, kElectionIdFieldName, jstOID, &electionIdElem);
396         if (!status.isOK()) {
397             return status;
398         }
399         _electionId = electionIdElem.OID();
400     }
401 
402     if (doc.hasField(kLastWriteFieldName)) {
403         BSONElement lastWriteElement;
404         status = bsonExtractTypedField(doc, kLastWriteFieldName, Object, &lastWriteElement);
405         if (!status.isOK()) {
406             return status;
407         }
408         BSONObj lastWriteObj = lastWriteElement.Obj();
409         bool lastWriteOpTimeSet = false;
410         bool lastWriteDateSet = false;
411         if (auto lastWriteOpTimeElement = lastWriteObj[kLastWriteOpTimeFieldName]) {
412             if (lastWriteOpTimeElement.type() != Object) {
413                 return Status(ErrorCodes::TypeMismatch,
414                               str::stream() << "Elements in \"" << kLastWriteOpTimeFieldName
415                                             << "\" obj "
416                                                "of isMaster response must be of type "
417                                             << typeName(Object)
418                                             << " but found type "
419                                             << typeName(lastWriteOpTimeElement.type()));
420             }
421             auto lastWriteOpTime = OpTime::parseFromOplogEntry(lastWriteOpTimeElement.Obj());
422             if (!lastWriteOpTime.isOK()) {
423                 return lastWriteOpTime.getStatus();
424             }
425             if (_lastWrite) {
426                 _lastWrite->opTime = lastWriteOpTime.getValue();
427             } else {
428                 _lastWrite = OpTimeWith<time_t>(0, lastWriteOpTime.getValue());
429             }
430             lastWriteOpTimeSet = true;
431         }
432         if (auto lastWriteDateElement = lastWriteObj[kLastWriteDateFieldName]) {
433             if (lastWriteDateElement.type() != Date) {
434                 return Status(ErrorCodes::TypeMismatch,
435                               str::stream() << "Elements in \"" << kLastWriteDateFieldName
436                                             << "\" obj "
437                                                "of isMaster response must be of type "
438                                             << typeName(Date)
439                                             << " but found type "
440                                             << typeName(lastWriteDateElement.type()));
441             }
442             if (_lastWrite) {
443                 _lastWrite->value = lastWriteDateElement.Date().toTimeT();
444             } else {
445                 _lastWrite = OpTimeWith<time_t>(lastWriteDateElement.Date().toTimeT(), OpTime());
446             }
447             lastWriteDateSet = true;
448         }
449         invariant(lastWriteOpTimeSet == lastWriteDateSet);
450 
451         bool lastMajorityWriteOpTimeSet = false;
452         bool lastMajorityWriteDateSet = false;
453         if (auto lastMajorityWriteOpTimeElement = lastWriteObj[kLastMajorityWriteOpTimeFieldName]) {
454             if (lastMajorityWriteOpTimeElement.type() != Object) {
455                 return Status(ErrorCodes::TypeMismatch,
456                               str::stream() << "Elements in \"" << kLastMajorityWriteOpTimeFieldName
457                                             << "\" obj "
458                                                "of isMaster response must be of type "
459                                             << typeName(Object)
460                                             << " but found type "
461                                             << typeName(lastMajorityWriteOpTimeElement.type()));
462             }
463             auto lastMajorityWriteOpTime =
464                 OpTime::parseFromOplogEntry(lastMajorityWriteOpTimeElement.Obj());
465             if (!lastMajorityWriteOpTime.isOK()) {
466                 return lastMajorityWriteOpTime.getStatus();
467             }
468             if (_lastMajorityWrite) {
469                 _lastMajorityWrite->opTime = lastMajorityWriteOpTime.getValue();
470             } else {
471                 _lastMajorityWrite = OpTimeWith<time_t>(0, lastMajorityWriteOpTime.getValue());
472             }
473             lastMajorityWriteOpTimeSet = true;
474         }
475         if (auto lastMajorityWriteDateElement = lastWriteObj[kLastMajorityWriteDateFieldName]) {
476             if (lastMajorityWriteDateElement.type() != Date) {
477                 return Status(ErrorCodes::TypeMismatch,
478                               str::stream() << "Elements in \"" << kLastMajorityWriteDateFieldName
479                                             << "\" obj "
480                                                "of isMaster response must be of type "
481                                             << typeName(Date)
482                                             << " but found type "
483                                             << typeName(lastMajorityWriteDateElement.type()));
484             }
485             if (_lastMajorityWrite) {
486                 _lastMajorityWrite->value = lastMajorityWriteDateElement.Date().toTimeT();
487             } else {
488                 _lastMajorityWrite =
489                     OpTimeWith<time_t>(lastMajorityWriteDateElement.Date().toTimeT(), OpTime());
490             }
491             lastMajorityWriteDateSet = true;
492         }
493         invariant(lastMajorityWriteOpTimeSet == lastMajorityWriteDateSet);
494     }
495 
496     std::string meString;
497     status = bsonExtractStringField(doc, kMeFieldName, &meString);
498     if (!status.isOK()) {
499         return status;
500     }
501     _me = HostAndPort(meString);
502     _meSet = true;
503 
504     return Status::OK();
505 }
506 
setIsMaster(bool isMaster)507 void IsMasterResponse::setIsMaster(bool isMaster) {
508     _isMasterSet = true;
509     _isMaster = isMaster;
510 }
511 
setIsSecondary(bool secondary)512 void IsMasterResponse::setIsSecondary(bool secondary) {
513     _isSecondarySet = true;
514     _secondary = secondary;
515 }
516 
setReplSetName(const std::string & setName)517 void IsMasterResponse::setReplSetName(const std::string& setName) {
518     _setNameSet = true;
519     _setName = setName;
520 }
521 
setReplSetVersion(long long version)522 void IsMasterResponse::setReplSetVersion(long long version) {
523     _setVersionSet = true;
524     _setVersion = version;
525 }
526 
addHost(const HostAndPort & host)527 void IsMasterResponse::addHost(const HostAndPort& host) {
528     _hostsSet = true;
529     _hosts.push_back(host);
530 }
531 
addPassive(const HostAndPort & passive)532 void IsMasterResponse::addPassive(const HostAndPort& passive) {
533     _passivesSet = true;
534     _passives.push_back(passive);
535 }
536 
addArbiter(const HostAndPort & arbiter)537 void IsMasterResponse::addArbiter(const HostAndPort& arbiter) {
538     _arbitersSet = true;
539     _arbiters.push_back(arbiter);
540 }
541 
setPrimary(const HostAndPort & primary)542 void IsMasterResponse::setPrimary(const HostAndPort& primary) {
543     _primarySet = true;
544     _primary = primary;
545 }
546 
setIsArbiterOnly(bool arbiterOnly)547 void IsMasterResponse::setIsArbiterOnly(bool arbiterOnly) {
548     _arbiterOnlySet = true;
549     _arbiterOnly = arbiterOnly;
550 }
551 
setIsPassive(bool passive)552 void IsMasterResponse::setIsPassive(bool passive) {
553     _passiveSet = true;
554     _passive = passive;
555 }
556 
setIsHidden(bool hidden)557 void IsMasterResponse::setIsHidden(bool hidden) {
558     _hiddenSet = true;
559     _hidden = hidden;
560 }
561 
setShouldBuildIndexes(bool buildIndexes)562 void IsMasterResponse::setShouldBuildIndexes(bool buildIndexes) {
563     _buildIndexesSet = true;
564     _buildIndexes = buildIndexes;
565 }
566 
setSlaveDelay(Seconds slaveDelay)567 void IsMasterResponse::setSlaveDelay(Seconds slaveDelay) {
568     _slaveDelaySet = true;
569     _slaveDelay = slaveDelay;
570 }
571 
addTag(const std::string & tagKey,const std::string & tagValue)572 void IsMasterResponse::addTag(const std::string& tagKey, const std::string& tagValue) {
573     _tagsSet = true;
574     _tags[tagKey] = tagValue;
575 }
576 
setMe(const HostAndPort & me)577 void IsMasterResponse::setMe(const HostAndPort& me) {
578     _meSet = true;
579     _me = me;
580 }
581 
setElectionId(const OID & electionId)582 void IsMasterResponse::setElectionId(const OID& electionId) {
583     _electionId = electionId;
584 }
585 
setLastWrite(const OpTime & lastWriteOpTime,const time_t lastWriteDate)586 void IsMasterResponse::setLastWrite(const OpTime& lastWriteOpTime, const time_t lastWriteDate) {
587     _lastWrite = OpTimeWith<time_t>(lastWriteDate, lastWriteOpTime);
588 }
589 
setLastMajorityWrite(const OpTime & lastMajorityWriteOpTime,const time_t lastMajorityWriteDate)590 void IsMasterResponse::setLastMajorityWrite(const OpTime& lastMajorityWriteOpTime,
591                                             const time_t lastMajorityWriteDate) {
592     _lastMajorityWrite = OpTimeWith<time_t>(lastMajorityWriteDate, lastMajorityWriteOpTime);
593 }
594 
markAsNoConfig()595 void IsMasterResponse::markAsNoConfig() {
596     _configSet = false;
597 }
598 
markAsShutdownInProgress()599 void IsMasterResponse::markAsShutdownInProgress() {
600     _shutdownInProgress = true;
601 }
602 
603 }  // namespace repl
604 }  // namespace mongo
605