Status OldUpdatePositionArgs::initialize(const BSONObj& argsObj) { Status status = bsonCheckOnlyHasFieldsForCommand( "OldUpdatePositionArgs", argsObj, kLegalUpdatePositionFieldNames); if (!status.isOK()) return status; // grab the array of changes BSONElement updateArray; status = bsonExtractTypedField(argsObj, kUpdateArrayFieldName, Array, &updateArray); if (!status.isOK()) return status; // now parse each array entry into an update BSONObjIterator i(updateArray.Obj()); while (i.more()) { BSONObj entry = i.next().Obj(); status = bsonCheckOnlyHasFields("UpdateInfoArgs", entry, kLegalUpdateInfoFieldNames); if (!status.isOK()) return status; OpTime opTime; if (entry[kOpTimeFieldName].isABSONObj()) { // In protocol version 1, { ts: <timestamp>, t: term } Status status = bsonExtractOpTimeField(entry, kOpTimeFieldName, &opTime); if (!status.isOK()) return status; } else { Timestamp ts; status = bsonExtractTimestampField(entry, kOpTimeFieldName, &ts); if (!status.isOK()) return status; opTime = OpTime(ts, OpTime::kUninitializedTerm); } if (!status.isOK()) return status; // TODO(spencer): The following three fields are optional in 3.0, but should be made // required or ignored in 3.0 long long cfgver; status = bsonExtractIntegerFieldWithDefault(entry, kConfigVersionFieldName, -1, &cfgver); if (!status.isOK()) return status; OID rid; status = bsonExtractOIDFieldWithDefault(entry, kMemberRIDFieldName, OID(), &rid); if (!status.isOK()) return status; long long memberID; status = bsonExtractIntegerFieldWithDefault(entry, kMemberIdFieldName, -1, &memberID); if (!status.isOK()) return status; _updates.push_back(UpdateInfo(rid, opTime, cfgver, memberID)); } return Status::OK(); }
StatusWith<ShardType> ShardType::fromBSON(const BSONObj& source) { ShardType shard; { std::string shardName; Status status = bsonExtractStringField(source, name.name(), &shardName); if (!status.isOK()) return status; shard._name = shardName; } { std::string shardHost; Status status = bsonExtractStringField(source, host.name(), &shardHost); if (!status.isOK()) return status; shard._host = shardHost; } { bool isShardDraining; Status status = bsonExtractBooleanFieldWithDefault(source, draining.name(), false, &isShardDraining); if (!status.isOK()) return status; shard._draining = isShardDraining; } { long long shardMaxSize; // maxSize == 0 means there's no limitation to space usage. Status status = bsonExtractIntegerFieldWithDefault(source, maxSize.name(), 0, &shardMaxSize); if (!status.isOK()) return status; shard._maxSize = shardMaxSize; } shard._tags = std::vector<std::string>(); if (source.hasField(tags.name())) { BSONElement tagsElement; Status status = bsonExtractTypedField(source, tags.name(), Array, &tagsElement); if (!status.isOK()) return status; BSONObjIterator it(tagsElement.Obj()); while (it.more()) { BSONElement tagElement = it.next(); if (tagElement.type() != String) { return Status(ErrorCodes::TypeMismatch, str::stream() << "Elements in \"" << tags.name() << "\" array must be strings but found " << typeName(tagElement.type())); } shard._tags->push_back(tagElement.String()); } } return shard; }
Status ShardingNetworkConnectionHook::validateHostImpl( const HostAndPort& remoteHost, const executor::RemoteCommandResponse& isMasterReply) { auto shard = grid.shardRegistry()->getShardNoReload(remoteHost.toString()); if (!shard) { return {ErrorCodes::ShardNotFound, str::stream() << "No shard found for host: " << remoteHost.toString()}; } long long configServerModeNumber; auto status = bsonExtractIntegerField(isMasterReply.data, "configsvr", &configServerModeNumber); switch (status.code()) { case ErrorCodes::OK: { // The ismaster response indicates remoteHost is a config server. if (!shard->isConfig()) { return {ErrorCodes::InvalidOptions, str::stream() << "Surprised to discover that " << remoteHost.toString() << " believes it is a config server"}; } using ConfigServerMode = CatalogManager::ConfigServerMode; const BSONElement setName = isMasterReply.data["setName"]; return grid.forwardingCatalogManager()->scheduleReplaceCatalogManagerIfNeeded( (configServerModeNumber == 0 ? ConfigServerMode::SCCC : ConfigServerMode::CSRS), (setName.type() == String ? setName.valueStringData() : StringData()), remoteHost); } case ErrorCodes::NoSuchKey: { // The ismaster response indicates that remoteHost is not a config server, or that // the config server is running a version prior to the 3.1 development series. if (!shard->isConfig()) { return Status::OK(); } long long remoteMaxWireVersion; status = bsonExtractIntegerFieldWithDefault(isMasterReply.data, "maxWireVersion", RELEASE_2_4_AND_BEFORE, &remoteMaxWireVersion); if (!status.isOK()) { return status; } if (remoteMaxWireVersion < FIND_COMMAND) { // Prior to the introduction of the find command and the 3.1 release series, it was // not possible to distinguish a config server from a shard server from its ismaster // response. As such, we must assume that the system is properly configured. return Status::OK(); } return {ErrorCodes::InvalidOptions, str::stream() << "Surprised to discover that " << remoteHost.toString() << " does not believe it is a config server"}; } default: // The ismaster response was malformed. return status; } }
Status ShardingNetworkConnectionHook::validateHostImpl( const HostAndPort& remoteHost, const executor::RemoteCommandResponse& isMasterReply) { auto shard = grid.shardRegistry()->getShardForHostNoReload(remoteHost); if (!shard) { return {ErrorCodes::ShardNotFound, str::stream() << "No shard found for host: " << remoteHost.toString()}; } long long configServerModeNumber; auto status = bsonExtractIntegerField(isMasterReply.data, "configsvr", &configServerModeNumber); // TODO SERVER-22320 fix should collapse the switch to only NoSuchKey handling switch (status.code()) { case ErrorCodes::OK: { // The ismaster response indicates remoteHost is a config server. if (!shard->isConfig()) { return {ErrorCodes::InvalidOptions, str::stream() << "Surprised to discover that " << remoteHost.toString() << " believes it is a config server"}; } return Status::OK(); } case ErrorCodes::NoSuchKey: { // The ismaster response indicates that remoteHost is not a config server, or that // the config server is running a version prior to the 3.1 development series. if (!shard->isConfig()) { return Status::OK(); } long long remoteMaxWireVersion; status = bsonExtractIntegerFieldWithDefault(isMasterReply.data, "maxWireVersion", RELEASE_2_4_AND_BEFORE, &remoteMaxWireVersion); if (!status.isOK()) { return status; } if (remoteMaxWireVersion < FIND_COMMAND) { // Prior to the introduction of the find command and the 3.1 release series, it was // not possible to distinguish a config server from a shard server from its ismaster // response. As such, we must assume that the system is properly configured. return Status::OK(); } return {ErrorCodes::InvalidOptions, str::stream() << "Surprised to discover that " << remoteHost.toString() << " does not believe it is a config server"}; } default: // The ismaster response was malformed. return status; } }
StatusWith<OpTime> OpTime::parseFromOplogEntry(const BSONObj& obj) { Timestamp ts; Status status = bsonExtractTimestampField(obj, kTimestampFieldName, &ts); if (!status.isOK()) return status; // Default to -1 if the term is absent. long long term; status = bsonExtractIntegerFieldWithDefault(obj, kTermFieldName, kUninitializedTerm, &term); if (!status.isOK()) return status; return OpTime(ts, term); }
Status bsonExtractIntegerFieldWithDefaultIf(const BSONObj& object, StringData fieldName, long long defaultValue, stdx::function<bool(long long)> pred, const std::string& predDescription, long long* out) { auto status = bsonExtractIntegerFieldWithDefault(object, fieldName, defaultValue, out); if (!status.isOK()) { return status; } if (!pred(*out)) { return Status(ErrorCodes::BadValue, mongoutils::str::stream() << "Invalid value in field \"" << fieldName << "\": " << *out << ": " << predDescription); } return Status::OK(); }
Status ReplSetHeartbeatArgs::initialize(const BSONObj& argsObj) { Status status = bsonCheckOnlyHasFields("ReplSetHeartbeatArgs", argsObj, kLegalHeartbeatFieldNames); if (!status.isOK()) return status; status = bsonExtractBooleanFieldWithDefault(argsObj, kCheckEmptyFieldName, false, &_checkEmpty); if (!status.isOK()) return status; _hasCheckEmpty = true; status = bsonExtractIntegerField(argsObj, kProtocolVersionFieldName, &_protocolVersion); if (!status.isOK()) return status; _hasProtocolVersion = true; status = bsonExtractIntegerField(argsObj, kConfigVersionFieldName, &_configVersion); if (!status.isOK()) return status; _hasConfigVersion = true; status = bsonExtractIntegerFieldWithDefault(argsObj, kSenderIdFieldName, -1, &_senderId); if (!status.isOK()) return status; _hasSenderId = true; status = bsonExtractStringField(argsObj, kSetNameFieldName, &_setName); if (!status.isOK()) return status; _hasSetName = true; std::string hostAndPortString; status = bsonExtractStringFieldWithDefault(argsObj, kSenderHostFieldName, "", &hostAndPortString); if (!status.isOK()) return status; if (!hostAndPortString.empty()) { status = _senderHost.initialize(hostAndPortString); if (!status.isOK()) return status; _hasSenderHost = true; } return Status::OK(); }
Status parseAuthSchemaUpgradeStepCommand(const BSONObj& cmdObj, const std::string& dbname, int* maxSteps, bool* shouldUpgradeShards, BSONObj* parsedWriteConcern) { static const int minUpgradeSteps = 1; static const int maxUpgradeSteps = 2; unordered_set<std::string> validFieldNames; validFieldNames.insert("authSchemaUpgrade"); validFieldNames.insert("maxSteps"); validFieldNames.insert("upgradeShards"); validFieldNames.insert("writeConcern"); Status status = _checkNoExtraFields(cmdObj, "authSchemaUpgrade", validFieldNames); if (!status.isOK()) { return status; } status = bsonExtractBooleanFieldWithDefault( cmdObj, "upgradeShards", true, shouldUpgradeShards); if (!status.isOK()) { return status; } long long steps; status = bsonExtractIntegerFieldWithDefault(cmdObj, "maxSteps", maxUpgradeSteps, &steps); if (!status.isOK()) return status; if (steps < minUpgradeSteps || steps > maxUpgradeSteps) { return Status(ErrorCodes::BadValue, mongoutils::str::stream() << "Legal values for \"maxSteps\" are at least " << minUpgradeSteps << " and no more than " << maxUpgradeSteps << "; found " << steps); } *maxSteps = static_cast<int>(steps); status = _extractWriteConcern(cmdObj, parsedWriteConcern); if (!status.isOK()) { return status; } return Status::OK(); }
StatusWith<ReadPreferenceSetting> ReadPreferenceSetting::fromBSON(const BSONObj& readPrefObj) { std::string modeStr; auto modeExtractStatus = bsonExtractStringField(readPrefObj, kModeFieldName, &modeStr); if (!modeExtractStatus.isOK()) { return modeExtractStatus; } ReadPreference mode; auto swReadPrefMode = parseReadPreferenceMode(modeStr); if (!swReadPrefMode.isOK()) { return swReadPrefMode.getStatus(); } mode = std::move(swReadPrefMode.getValue()); TagSet tags; BSONElement tagsElem; auto tagExtractStatus = bsonExtractTypedField(readPrefObj, kTagsFieldName, mongo::Array, &tagsElem); if (tagExtractStatus.isOK()) { tags = TagSet{BSONArray(tagsElem.Obj().getOwned())}; // In accordance with the read preference spec, passing the default wildcard tagset // '[{}]' is the same as not passing a TagSet at all. Furthermore, passing an empty // TagSet with a non-primary ReadPreference is equivalent to passing the wildcard // ReadPreference. if (tags == TagSet() || tags == TagSet::primaryOnly()) { tags = defaultTagSetForMode(mode); } // If we are using a user supplied TagSet, check that it is compatible with // the readPreference mode. else if (ReadPreference::PrimaryOnly == mode && (tags != TagSet::primaryOnly())) { return Status(ErrorCodes::BadValue, "Only empty tags are allowed with primary read preference"); } } else if (ErrorCodes::NoSuchKey == tagExtractStatus) { tags = defaultTagSetForMode(mode); } else { return tagExtractStatus; } long long maxStalenessMSValue; auto maxStalenessMSExtractStatus = bsonExtractIntegerFieldWithDefault( readPrefObj, kMaxStalenessMSFieldName, 0, &maxStalenessMSValue); if (!maxStalenessMSExtractStatus.isOK()) { return maxStalenessMSExtractStatus; } if (maxStalenessMSValue < 0) { return Status(ErrorCodes::BadValue, str::stream() << kMaxStalenessMSFieldName << " must be a non negative integer"); } if (maxStalenessMSValue >= Milliseconds::max().count()) { return Status(ErrorCodes::BadValue, str::stream() << kMaxStalenessMSFieldName << " value can not exceed" << Milliseconds::max().count()); } return ReadPreferenceSetting(mode, tags, Milliseconds(maxStalenessMSValue)); }
Status ReplicaSetConfig::_parseSettingsSubdocument(const BSONObj& settings) { // // Parse heartbeatIntervalMillis // long long heartbeatIntervalMillis; Status hbIntervalStatus = bsonExtractIntegerFieldWithDefault(settings, kHeartbeatIntervalFieldName, durationCount<Milliseconds>(kDefaultHeartbeatInterval), &heartbeatIntervalMillis); if (!hbIntervalStatus.isOK()) { return hbIntervalStatus; } _heartbeatInterval = Milliseconds(heartbeatIntervalMillis); // Parse electionTimeoutMillis // BSONElement electionTimeoutMillisElement = settings[kElectionTimeoutFieldName]; if (electionTimeoutMillisElement.eoo()) { _electionTimeoutPeriod = Milliseconds(kDefaultElectionTimeoutPeriod); } else if (electionTimeoutMillisElement.isNumber()) { _electionTimeoutPeriod = Milliseconds(electionTimeoutMillisElement.numberInt()); } else { return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected type of " << kSettingsFieldName << "." << kElectionTimeoutFieldName << " to be a number, but found a value of type " << typeName(electionTimeoutMillisElement.type())); } // // Parse heartbeatTimeoutSecs // BSONElement hbTimeoutSecsElement = settings[kHeartbeatTimeoutFieldName]; if (hbTimeoutSecsElement.eoo()) { _heartbeatTimeoutPeriod = Seconds(kDefaultHeartbeatTimeoutPeriod); } else if (hbTimeoutSecsElement.isNumber()) { _heartbeatTimeoutPeriod = Seconds(hbTimeoutSecsElement.numberInt()); } else { return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected type of " << kSettingsFieldName << "." << kHeartbeatTimeoutFieldName << " to be a number, but found a value of type " << typeName(hbTimeoutSecsElement.type())); } // // Parse chainingAllowed // Status status = bsonExtractBooleanFieldWithDefault( settings, kChainingAllowedFieldName, true, &_chainingAllowed); if (!status.isOK()) return status; // // Parse getLastErrorDefaults // BSONElement gleDefaultsElement; status = bsonExtractTypedField( settings, kGetLastErrorDefaultsFieldName, Object, &gleDefaultsElement); if (status.isOK()) { status = _defaultWriteConcern.parse(gleDefaultsElement.Obj()); if (!status.isOK()) return status; } else if (status == ErrorCodes::NoSuchKey) { // Default write concern is w: 1. _defaultWriteConcern.reset(); _defaultWriteConcern.wNumNodes = 1; } else { return status; } // // Parse getLastErrorModes // BSONElement gleModesElement; status = bsonExtractTypedField(settings, kGetLastErrorModesFieldName, Object, &gleModesElement); BSONObj gleModes; if (status.isOK()) { gleModes = gleModesElement.Obj(); } else if (status != ErrorCodes::NoSuchKey) { return status; } for (BSONObj::iterator gleModeIter(gleModes); gleModeIter.more();) { const BSONElement modeElement = gleModeIter.next(); if (_customWriteConcernModes.find(modeElement.fieldNameStringData()) != _customWriteConcernModes.end()) { return Status(ErrorCodes::DuplicateKey, str::stream() << kSettingsFieldName << '.' << kGetLastErrorModesFieldName << " contains multiple fields named " << modeElement.fieldName()); } if (modeElement.type() != Object) { return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected " << kSettingsFieldName << '.' << kGetLastErrorModesFieldName << '.' << modeElement.fieldName() << " to be an Object, not " << typeName(modeElement.type())); } ReplicaSetTagPattern pattern = _tagConfig.makePattern(); for (BSONObj::iterator constraintIter(modeElement.Obj()); constraintIter.more();) { const BSONElement constraintElement = constraintIter.next(); if (!constraintElement.isNumber()) { return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected " << kSettingsFieldName << '.' << kGetLastErrorModesFieldName << '.' << modeElement.fieldName() << '.' << constraintElement.fieldName() << " to be a number, not " << typeName(constraintElement.type())); } const int minCount = constraintElement.numberInt(); if (minCount <= 0) { return Status(ErrorCodes::BadValue, str::stream() << "Value of " << kSettingsFieldName << '.' << kGetLastErrorModesFieldName << '.' << modeElement.fieldName() << '.' << constraintElement.fieldName() << " must be positive, but found " << minCount); } status = _tagConfig.addTagCountConstraintToPattern( &pattern, constraintElement.fieldNameStringData(), minCount); if (!status.isOK()) { return status; } } _customWriteConcernModes[modeElement.fieldNameStringData()] = pattern; } return Status::OK(); }
StatusWith<BalanceChunkRequest> BalanceChunkRequest::parseFromConfigCommand(const BSONObj& obj) { auto chunkStatus = ChunkType::fromBSON(obj); if (!chunkStatus.isOK()) { return chunkStatus.getStatus(); } // The secondary throttle options being sent to the config server are contained within a // sub-object on the request because they contain the writeConcern field, which when sent to the // config server gets checked for only being w:1 or w:majoirty. BSONObj secondaryThrottleObj; { BSONElement secondaryThrottleElement; auto secondaryThrottleElementStatus = bsonExtractTypedField(obj, kSecondaryThrottle, Object, &secondaryThrottleElement); if (secondaryThrottleElementStatus.isOK()) { secondaryThrottleObj = secondaryThrottleElement.Obj(); } else if (secondaryThrottleElementStatus != ErrorCodes::NoSuchKey) { return secondaryThrottleElementStatus; } } auto secondaryThrottleStatus = MigrationSecondaryThrottleOptions::createFromCommand(secondaryThrottleObj); if (!secondaryThrottleStatus.isOK()) { return secondaryThrottleStatus.getStatus(); } BalanceChunkRequest request(std::move(chunkStatus.getValue()), std::move(secondaryThrottleStatus.getValue())); { Status status = bsonExtractBooleanFieldWithDefault(obj, kWaitForDelete, false, &request._waitForDelete); if (!status.isOK()) { return status; } } { long long maxChunkSizeBytes; Status status = bsonExtractIntegerFieldWithDefault(obj, kMaxChunkSizeBytes, 0, &maxChunkSizeBytes); if (!status.isOK()) { return status; } request._maxChunkSizeBytes = static_cast<int64_t>(maxChunkSizeBytes); } { std::string toShardId; Status status = bsonExtractStringField(obj, kToShardId, &toShardId); if (status.isOK()) { if (toShardId.empty()) { return {ErrorCodes::BadValue, "To shard cannot be empty"}; } request._toShardId = std::move(toShardId); } else if (status != ErrorCodes::NoSuchKey) { return status; } } return request; }