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::kCommand
32 
33 #include "mongo/platform/basic.h"
34 
35 #include "mongo/db/commands/feature_compatibility_version.h"
36 
37 #include "mongo/base/status.h"
38 #include "mongo/db/dbdirectclient.h"
39 #include "mongo/db/operation_context.h"
40 #include "mongo/db/repl/optime.h"
41 #include "mongo/db/repl/storage_interface.h"
42 #include "mongo/db/server_parameters.h"
43 #include "mongo/db/service_context.h"
44 #include "mongo/db/storage/storage_engine.h"
45 #include "mongo/db/wire_version.h"
46 #include "mongo/db/write_concern_options.h"
47 #include "mongo/rpc/get_status_from_command_result.h"
48 #include "mongo/transport/service_entry_point.h"
49 #include "mongo/util/log.h"
50 
51 namespace mongo {
52 
53 using repl::UnreplicatedWritesBlock;
54 
55 constexpr StringData FeatureCompatibilityVersion::kCollection;
56 constexpr StringData FeatureCompatibilityVersion::kCommandName;
57 constexpr StringData FeatureCompatibilityVersion::kDatabase;
58 constexpr StringData FeatureCompatibilityVersion::kParameterName;
59 constexpr StringData FeatureCompatibilityVersion::kVersionField;
60 constexpr StringData FeatureCompatibilityVersion::kTargetVersionField;
61 
62 Lock::ResourceMutex FeatureCompatibilityVersion::fcvLock("featureCompatibilityVersionLock");
63 
parse(const BSONObj & featureCompatibilityVersionDoc)64 StatusWith<ServerGlobalParams::FeatureCompatibility::Version> FeatureCompatibilityVersion::parse(
65     const BSONObj& featureCompatibilityVersionDoc) {
66     ServerGlobalParams::FeatureCompatibility::Version version =
67         ServerGlobalParams::FeatureCompatibility::Version::kUnsetDefault34Behavior;
68     std::string versionString;
69     std::string targetVersionString;
70 
71     for (auto&& elem : featureCompatibilityVersionDoc) {
72         auto fieldName = elem.fieldNameStringData();
73         if (fieldName == "_id") {
74             continue;
75         } else if (fieldName == FeatureCompatibilityVersion::kVersionField ||
76                    fieldName == FeatureCompatibilityVersion::kTargetVersionField) {
77             if (elem.type() != BSONType::String) {
78                 return Status(ErrorCodes::TypeMismatch,
79                               str::stream() << fieldName
80                                             << " must be of type String, but was of type "
81                                             << typeName(elem.type())
82                                             << ". Contents of "
83                                             << FeatureCompatibilityVersion::kParameterName
84                                             << " document in "
85                                             << FeatureCompatibilityVersion::kCollection
86                                             << ": "
87                                             << featureCompatibilityVersionDoc
88                                             << ". See "
89                                             << feature_compatibility_version::kDochubLink
90                                             << ".");
91             }
92 
93             if (elem.String() != FeatureCompatibilityVersionCommandParser::kVersion36 &&
94                 elem.String() != FeatureCompatibilityVersionCommandParser::kVersion34) {
95                 return Status(ErrorCodes::BadValue,
96                               str::stream() << "Invalid value for " << fieldName << ", found "
97                                             << elem.String()
98                                             << ", expected '"
99                                             << FeatureCompatibilityVersionCommandParser::kVersion36
100                                             << "' or '"
101                                             << FeatureCompatibilityVersionCommandParser::kVersion34
102                                             << "'. Contents of "
103                                             << FeatureCompatibilityVersion::kParameterName
104                                             << " document in "
105                                             << FeatureCompatibilityVersion::kCollection
106                                             << ": "
107                                             << featureCompatibilityVersionDoc
108                                             << ". See "
109                                             << feature_compatibility_version::kDochubLink
110                                             << ".");
111             }
112 
113             if (fieldName == FeatureCompatibilityVersion::kVersionField) {
114                 versionString = elem.String();
115             } else if (fieldName == FeatureCompatibilityVersion::kTargetVersionField) {
116                 targetVersionString = elem.String();
117             }
118         } else {
119             return Status(ErrorCodes::BadValue,
120                           str::stream() << "Unrecognized field '" << fieldName << "'. Contents of "
121                                         << FeatureCompatibilityVersion::kParameterName
122                                         << " document in "
123                                         << FeatureCompatibilityVersion::kCollection
124                                         << ": "
125                                         << featureCompatibilityVersionDoc
126                                         << ". See "
127                                         << feature_compatibility_version::kDochubLink
128                                         << ".");
129         }
130     }
131 
132     if (versionString == FeatureCompatibilityVersionCommandParser::kVersion34) {
133         if (targetVersionString == FeatureCompatibilityVersionCommandParser::kVersion36) {
134             version = ServerGlobalParams::FeatureCompatibility::Version::kUpgradingTo36;
135         } else if (targetVersionString == FeatureCompatibilityVersionCommandParser::kVersion34) {
136             version = ServerGlobalParams::FeatureCompatibility::Version::kDowngradingTo34;
137         } else {
138             version = ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo34;
139         }
140 
141     } else if (versionString == FeatureCompatibilityVersionCommandParser::kVersion36) {
142         if (targetVersionString == FeatureCompatibilityVersionCommandParser::kVersion36 ||
143             targetVersionString == FeatureCompatibilityVersionCommandParser::kVersion34) {
144             return Status(ErrorCodes::BadValue,
145                           str::stream() << "Invalid state for "
146                                         << FeatureCompatibilityVersion::kParameterName
147                                         << " document in "
148                                         << FeatureCompatibilityVersion::kCollection
149                                         << ": "
150                                         << featureCompatibilityVersionDoc
151                                         << ". See "
152                                         << feature_compatibility_version::kDochubLink
153                                         << ".");
154         } else {
155             version = ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo36;
156         }
157     } else {
158         return Status(ErrorCodes::BadValue,
159                       str::stream() << "Missing required field '"
160                                     << FeatureCompatibilityVersion::kVersionField
161                                     << "''. Contents of "
162                                     << FeatureCompatibilityVersion::kParameterName
163                                     << " document in "
164                                     << FeatureCompatibilityVersion::kCollection
165                                     << ": "
166                                     << featureCompatibilityVersionDoc
167                                     << ". See "
168                                     << feature_compatibility_version::kDochubLink
169                                     << ".");
170     }
171 
172     return version;
173 }
174 
setTargetUpgrade(OperationContext * opCtx)175 void FeatureCompatibilityVersion::setTargetUpgrade(OperationContext* opCtx) {
176     // Sets both 'version' and 'targetVersion' fields.
177     _runUpdateCommand(opCtx, [](auto updateMods) {
178         updateMods.append(FeatureCompatibilityVersion::kVersionField,
179                           FeatureCompatibilityVersionCommandParser::kVersion34);
180         updateMods.append(FeatureCompatibilityVersion::kTargetVersionField,
181                           FeatureCompatibilityVersionCommandParser::kVersion36);
182     });
183 }
184 
setTargetDowngrade(OperationContext * opCtx)185 void FeatureCompatibilityVersion::setTargetDowngrade(OperationContext* opCtx) {
186     // Sets both 'version' and 'targetVersion' fields.
187     _runUpdateCommand(opCtx, [](auto updateMods) {
188         updateMods.append(FeatureCompatibilityVersion::kVersionField,
189                           FeatureCompatibilityVersionCommandParser::kVersion34);
190         updateMods.append(FeatureCompatibilityVersion::kTargetVersionField,
191                           FeatureCompatibilityVersionCommandParser::kVersion34);
192     });
193 }
194 
unsetTargetUpgradeOrDowngrade(OperationContext * opCtx,StringData version)195 void FeatureCompatibilityVersion::unsetTargetUpgradeOrDowngrade(OperationContext* opCtx,
196                                                                 StringData version) {
197     _validateVersion(version);
198 
199     // Updates 'version' field, while also unsetting the 'targetVersion' field.
200     _runUpdateCommand(opCtx, [version](auto updateMods) {
201         updateMods.append(FeatureCompatibilityVersion::kVersionField, version);
202     });
203 }
204 
setIfCleanStartup(OperationContext * opCtx,repl::StorageInterface * storageInterface)205 void FeatureCompatibilityVersion::setIfCleanStartup(OperationContext* opCtx,
206                                                     repl::StorageInterface* storageInterface) {
207     if (!isCleanStartUp())
208         return;
209 
210     // If the server was not started with --shardsvr, the default featureCompatibilityVersion on
211     // clean startup is the upgrade version. If it was started with --shardsvr, the default
212     // featureCompatibilityVersion is the downgrade version, so that it can be safely added to a
213     // downgrade version cluster. The config server will run setFeatureCompatibilityVersion as part
214     // of addShard.
215     const bool storeUpgradeVersion = serverGlobalParams.clusterRole != ClusterRole::ShardServer;
216 
217     UnreplicatedWritesBlock unreplicatedWritesBlock(opCtx);
218     NamespaceString nss(FeatureCompatibilityVersion::kCollection);
219 
220     {
221         CollectionOptions options;
222         options.uuid = CollectionUUID::gen();
223 
224         // Only for 3.4 shard servers, we create admin.system.version without UUID on clean startup.
225         // The mention of kFullyDowngradedTo34 below is to ensure this will not compile in 3.8.
226         if (!storeUpgradeVersion &&
227             serverGlobalParams.featureCompatibility.getVersion() ==
228                 ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo34)
229             options.uuid.reset();
230 
231         uassertStatusOK(storageInterface->createCollection(opCtx, nss, options));
232     }
233 
234     // We then insert the featureCompatibilityVersion document into the "admin.system.version"
235     // collection. The server parameter will be updated on commit by the op observer.
236     uassertStatusOK(storageInterface->insertDocument(
237         opCtx,
238         nss,
239         repl::TimestampedBSONObj{
240             BSON("_id" << FeatureCompatibilityVersion::kParameterName
241                        << FeatureCompatibilityVersion::kVersionField
242                        << (storeUpgradeVersion
243                                ? FeatureCompatibilityVersionCommandParser::kVersion36
244                                : FeatureCompatibilityVersionCommandParser::kVersion34)),
245             Timestamp()},
246         repl::OpTime::kUninitializedTerm));  // No timestamp or term because this write is not
247                                              // replicated.
248 }
249 
isCleanStartUp()250 bool FeatureCompatibilityVersion::isCleanStartUp() {
251     std::vector<std::string> dbNames;
252     StorageEngine* storageEngine = getGlobalServiceContext()->getGlobalStorageEngine();
253     storageEngine->listDatabases(&dbNames);
254 
255     for (auto&& dbName : dbNames) {
256         if (dbName != "local") {
257             return false;
258         }
259     }
260     return true;
261 }
262 
263 namespace {
264 
265 // If a node finishes rollback while in fcv=3.6, but while in RECOVERING before reaching
266 // minValid, sees that its sync source started a downgrade to fcv=3.4, it may be be unable to
267 // become consistent. This is because an fcv=3.6 node will have used the UUID algorithm for
268 // rollback, which treats a missing UUID as a collection drop. We fassert before becoming
269 // SECONDARY in an inconsistent state.
270 //
271 // We only care about the start of downgrade, because if we see an oplog entry marking the end
272 // of downgrade, then either we also saw a downgrade start op, or we did our rollback in fcv=3.4
273 // with the no-UUID algorithm. Similarly, we do not care about upgrade, because either
274 // it will also have a downgrade op, or we did our rollback in fcv=3.4. The no-UUID rollback
275 // algorithm will be as safe as it was in 3.4 regardless of if the sync source has UUIDs or not.
uassertDuringRollbackOnDowngradeOp(OperationContext * opCtx,ServerGlobalParams::FeatureCompatibility::Version newVersion,std::string msg)276 void uassertDuringRollbackOnDowngradeOp(
277     OperationContext* opCtx,
278     ServerGlobalParams::FeatureCompatibility::Version newVersion,
279     std::string msg) {
280     if ((newVersion != ServerGlobalParams::FeatureCompatibility::Version::kDowngradingTo34) ||
281         (serverGlobalParams.featureCompatibility.getVersion() !=
282          ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo36)) {
283         // This is only the start of a downgrade operation if we're currently upgraded to 3.6.
284         return;
285     }
286 
287     auto replCoord = repl::ReplicationCoordinator::get(opCtx);
288     uassert(ErrorCodes::OplogOperationUnsupported,
289             msg,
290             replCoord->getReplicationMode() != repl::ReplicationCoordinator::modeReplSet ||
291                 replCoord->isInPrimaryOrSecondaryState());
292 }
293 
294 // If we are finishing an upgrade, all collections should have UUIDs. We check to make sure
295 // that the feature compatibility version collection has a UUID.
uassertDuringInvalidUpgradeOp(OperationContext * opCtx,ServerGlobalParams::FeatureCompatibility::Version version)296 void uassertDuringInvalidUpgradeOp(OperationContext* opCtx,
297                                    ServerGlobalParams::FeatureCompatibility::Version version) {
298     auto replCoord = repl::ReplicationCoordinator::get(opCtx);
299     if (replCoord->getReplicationMode() == repl::ReplicationCoordinator::modeMasterSlave) {
300         // TODO (SERVER-31593) master-slave should uassert here, but cannot due to a bug.
301         return;
302     }
303 
304     if (version != ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo36) {
305         // Only check this assertion if we're completing an upgrade to 3.6.
306         return;
307     }
308 
309     auto fcvUUID = repl::StorageInterface::get(opCtx)->getCollectionUUID(
310         opCtx, NamespaceString(FeatureCompatibilityVersion::kCollection));
311     uassert(ErrorCodes::IllegalOperation,
312             str::stream() << "Error checking for UUID in feature compatibility version collection: "
313                           << fcvUUID.getStatus().toString(),
314             fcvUUID.isOK());
315     uassert(ErrorCodes::IllegalOperation,
316             str::stream() << "Tried to complete upgrade, but "
317                           << FeatureCompatibilityVersion::kCollection
318                           << " did not have a UUID.",
319             fcvUUID.getValue());
320 }
321 
322 }  // namespace
323 
onInsertOrUpdate(OperationContext * opCtx,const BSONObj & doc)324 void FeatureCompatibilityVersion::onInsertOrUpdate(OperationContext* opCtx, const BSONObj& doc) {
325     auto idElement = doc["_id"];
326     if (idElement.type() != BSONType::String ||
327         idElement.String() != FeatureCompatibilityVersion::kParameterName) {
328         return;
329     }
330     auto newVersion = uassertStatusOK(FeatureCompatibilityVersion::parse(doc));
331 
332     uassertDuringRollbackOnDowngradeOp(opCtx,
333                                        newVersion,
334                                        str::stream()
335                                            << "Must be in primary or secondary state to "
336                                               "downgrade feature compatibility version document: "
337                                            << redact(doc));
338     uassertDuringInvalidUpgradeOp(opCtx, newVersion);
339 
340     // To avoid extra log messages when the targetVersion is set/unset, only log when the version
341     // changes.
342     auto oldVersion = serverGlobalParams.featureCompatibility.getVersion();
343     if (oldVersion != newVersion) {
344         log() << "setting featureCompatibilityVersion to " << toString(newVersion);
345     }
346 
347     // On commit, update the server parameters, and close any incoming connections with a wire
348     // version that is below the minimum.
349     opCtx->recoveryUnit()->onCommit([opCtx, newVersion]() {
350         serverGlobalParams.featureCompatibility.setVersion(newVersion);
351         updateMinWireVersion();
352 
353         // Close all incoming connections from internal clients with binary versions lower than
354         // ours. It would be desirable to close all outgoing connections to servers with lower
355         // binary version, but it is not currently possible.
356         if (newVersion != ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo34) {
357             opCtx->getServiceContext()->getServiceEntryPoint()->endAllSessions(
358                 transport::Session::kLatestVersionInternalClientKeepOpen |
359                 transport::Session::kExternalClientKeepOpen);
360         }
361     });
362 }
363 
onDelete(OperationContext * opCtx,const BSONObj & doc)364 void FeatureCompatibilityVersion::onDelete(OperationContext* opCtx, const BSONObj& doc) {
365     auto idElement = doc["_id"];
366     if (idElement.type() != BSONType::String ||
367         idElement.String() != FeatureCompatibilityVersion::kParameterName) {
368         return;
369     }
370 
371     uassertDuringRollbackOnDowngradeOp(
372         opCtx,
373         ServerGlobalParams::FeatureCompatibility::Version::kDowngradingTo34,
374         "Must be in primary or secondary state to delete feature compatibility version document");
375 
376     log() << "setting featureCompatibilityVersion to "
377           << FeatureCompatibilityVersionCommandParser::kVersion34;
378     opCtx->recoveryUnit()->onCommit([]() {
379         serverGlobalParams.featureCompatibility.setVersion(
380             ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo34);
381         updateMinWireVersion();
382     });
383 }
384 
onDropCollection(OperationContext * opCtx)385 void FeatureCompatibilityVersion::onDropCollection(OperationContext* opCtx) {
386 
387     uassertDuringRollbackOnDowngradeOp(
388         opCtx,
389         ServerGlobalParams::FeatureCompatibility::Version::kDowngradingTo34,
390         str::stream() << "Must be in primary or secondary state to drop "
391                       << FeatureCompatibilityVersion::kCollection
392                       << " collection");
393 
394     log() << "setting featureCompatibilityVersion to "
395           << FeatureCompatibilityVersionCommandParser::kVersion34;
396     opCtx->recoveryUnit()->onCommit([]() {
397         serverGlobalParams.featureCompatibility.setVersion(
398             ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo34);
399         updateMinWireVersion();
400     });
401 }
402 
updateMinWireVersion()403 void FeatureCompatibilityVersion::updateMinWireVersion() {
404     WireSpec& spec = WireSpec::instance();
405 
406     switch (serverGlobalParams.featureCompatibility.getVersion()) {
407         case ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo36:
408         case ServerGlobalParams::FeatureCompatibility::Version::kUpgradingTo36:
409         case ServerGlobalParams::FeatureCompatibility::Version::kDowngradingTo34:
410             spec.incomingInternalClient.minWireVersion = LATEST_WIRE_VERSION;
411             spec.outgoing.minWireVersion = LATEST_WIRE_VERSION;
412             return;
413         case ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo34:
414             // It would be preferable to set 'incomingInternalClient.minWireVersion' and
415             // 'outgoing.minWireVersion' to LATEST_WIRE_VERSION - 1, but this is not possible due to
416             // a bug in 3.4, where if the receiving node says it supports wire version range
417             // [COMMANDS_ACCEPT_WRITE_CONCERN, SUPPORTS_OP_MSG], the initiating node will think it
418             // only supports OP_QUERY.
419             spec.incomingInternalClient.minWireVersion = RELEASE_2_4_AND_BEFORE;
420             spec.outgoing.minWireVersion = RELEASE_2_4_AND_BEFORE;
421             return;
422         case ServerGlobalParams::FeatureCompatibility::Version::kUnsetDefault34Behavior:
423             // getVersion() does not return this value.
424             MONGO_UNREACHABLE;
425     }
426 }
427 
_validateVersion(StringData version)428 void FeatureCompatibilityVersion::_validateVersion(StringData version) {
429     uassert(40284,
430             str::stream() << "featureCompatibilityVersion must be '"
431                           << FeatureCompatibilityVersionCommandParser::kVersion36
432                           << "' or '"
433                           << FeatureCompatibilityVersionCommandParser::kVersion34
434                           << "'. See "
435                           << feature_compatibility_version::kDochubLink
436                           << ".",
437             version == FeatureCompatibilityVersionCommandParser::kVersion36 ||
438                 version == FeatureCompatibilityVersionCommandParser::kVersion34);
439 }
440 
_runUpdateCommand(OperationContext * opCtx,UpdateBuilder builder)441 void FeatureCompatibilityVersion::_runUpdateCommand(OperationContext* opCtx,
442                                                     UpdateBuilder builder) {
443     DBDirectClient client(opCtx);
444     NamespaceString nss(FeatureCompatibilityVersion::kCollection);
445 
446     BSONObjBuilder updateCmd;
447     updateCmd.append("update", nss.coll());
448     {
449         BSONArrayBuilder updates(updateCmd.subarrayStart("updates"));
450         {
451             BSONObjBuilder updateSpec(updates.subobjStart());
452             {
453                 BSONObjBuilder queryFilter(updateSpec.subobjStart("q"));
454                 queryFilter.append("_id", FeatureCompatibilityVersion::kParameterName);
455             }
456             {
457                 BSONObjBuilder updateMods(updateSpec.subobjStart("u"));
458                 builder(std::move(updateMods));
459             }
460             updateSpec.appendBool("upsert", true);
461         }
462     }
463     updateCmd.append(WriteConcernOptions::kWriteConcernField, WriteConcernOptions::Majority);
464 
465     // Update the featureCompatibilityVersion document stored in the "admin.system.version"
466     // collection.
467     BSONObj updateResult;
468     client.runCommand(nss.db().toString(), updateCmd.obj(), updateResult);
469     uassertStatusOK(getStatusFromWriteCommandReply(updateResult));
470 }
471 /**
472  * Read-only server parameter for featureCompatibilityVersion.
473  */
474 class FeatureCompatibilityVersionParameter : public ServerParameter {
475 public:
FeatureCompatibilityVersionParameter()476     FeatureCompatibilityVersionParameter()
477         : ServerParameter(ServerParameterSet::getGlobal(),
478                           FeatureCompatibilityVersion::kParameterName.toString(),
479                           false,  // allowedToChangeAtStartup
480                           false   // allowedToChangeAtRuntime
481                           ) {}
482 
append(OperationContext * opCtx,BSONObjBuilder & b,const std::string & name)483     virtual void append(OperationContext* opCtx, BSONObjBuilder& b, const std::string& name) {
484         BSONObjBuilder featureCompatibilityVersionBuilder(b.subobjStart(name));
485         switch (serverGlobalParams.featureCompatibility.getVersion()) {
486             case ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo36:
487                 featureCompatibilityVersionBuilder.append(
488                     FeatureCompatibilityVersion::kVersionField,
489                     FeatureCompatibilityVersionCommandParser::kVersion36);
490                 return;
491             case ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo34:
492                 featureCompatibilityVersionBuilder.append(
493                     FeatureCompatibilityVersion::kVersionField,
494                     FeatureCompatibilityVersionCommandParser::kVersion34);
495                 return;
496             case ServerGlobalParams::FeatureCompatibility::Version::kUpgradingTo36:
497                 featureCompatibilityVersionBuilder.append(
498                     FeatureCompatibilityVersion::kVersionField,
499                     FeatureCompatibilityVersionCommandParser::kVersion34);
500                 featureCompatibilityVersionBuilder.append(
501                     FeatureCompatibilityVersion::kTargetVersionField,
502                     FeatureCompatibilityVersionCommandParser::kVersion36);
503                 return;
504             case ServerGlobalParams::FeatureCompatibility::Version::kDowngradingTo34:
505                 featureCompatibilityVersionBuilder.append(
506                     FeatureCompatibilityVersion::kVersionField,
507                     FeatureCompatibilityVersionCommandParser::kVersion34);
508                 featureCompatibilityVersionBuilder.append(
509                     FeatureCompatibilityVersion::kTargetVersionField,
510                     FeatureCompatibilityVersionCommandParser::kVersion34);
511                 return;
512             case ServerGlobalParams::FeatureCompatibility::Version::kUnsetDefault34Behavior:
513                 // getVersion() does not return this value.
514                 MONGO_UNREACHABLE;
515         }
516     }
517 
set(const BSONElement & newValueElement)518     virtual Status set(const BSONElement& newValueElement) {
519         return Status(ErrorCodes::IllegalOperation,
520                       str::stream() << FeatureCompatibilityVersion::kParameterName
521                                     << " cannot be set via setParameter. See "
522                                     << feature_compatibility_version::kDochubLink
523                                     << ".");
524     }
525 
setFromString(const std::string & str)526     virtual Status setFromString(const std::string& str) {
527         return Status(ErrorCodes::IllegalOperation,
528                       str::stream() << FeatureCompatibilityVersion::kParameterName
529                                     << " cannot be set via setParameter. See "
530                                     << feature_compatibility_version::kDochubLink
531                                     << ".");
532     }
533 } featureCompatibilityVersionParameter;
534 
535 MONGO_EXPORT_STARTUP_SERVER_PARAMETER(internalValidateFeaturesAsMaster, bool, true);
536 
537 }  // namespace mongo
538