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();
}
Exemple #2
0
    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;
    }
}
Exemple #5
0
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));
}
Exemple #10
0
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;
}