Status ReplSetRequestVotesResponse::initialize(const BSONObj& argsObj) {
        Status status = bsonCheckOnlyHasFields("ReplSetRequestVotes",
                                               argsObj,
                                               kLegalResponseFieldNames);
        if (!status.isOK())
            return status;

        status = bsonExtractIntegerField(argsObj, kTermFieldName, &_term);
        if (!status.isOK())
            return status;

        status = bsonExtractBooleanField(argsObj, kVoteGrantedFieldName, &_voteGranted);
        if (!status.isOK())
            return status;

        status = bsonExtractStringField(argsObj, kReasonFieldName, &_reason);
        if (!status.isOK())
            return status;

        status = bsonExtractBooleanField(argsObj, kOkFieldName, &_ok);
        if (!status.isOK())
            return status;

        return Status::OK();
    }
StatusWith<MigrationSecondaryThrottleOptions> MigrationSecondaryThrottleOptions::createFromCommand(
    const BSONObj& obj) {
    SecondaryThrottleOption secondaryThrottle;
    boost::optional<BSONObj> writeConcernBSON;

    // Parse the two variants of the 'secondaryThrottle' option
    {
        bool isSecondaryThrottle;

        Status status =
            bsonExtractBooleanField(obj, kSecondaryThrottleMongod, &isSecondaryThrottle);
        if (status == ErrorCodes::NoSuchKey) {
            status = bsonExtractBooleanField(obj, kSecondaryThrottleMongos, &isSecondaryThrottle);
        }

        if (status == ErrorCodes::NoSuchKey) {
            secondaryThrottle = kDefault;
        } else if (status.isOK()) {
            secondaryThrottle = (isSecondaryThrottle ? kOn : kOff);
        } else {
            return status;
        }
    }

    // Extract the requested 'writeConcern' option
    {
        BSONElement writeConcernElem;
        Status status = bsonExtractField(obj, kWriteConcern, &writeConcernElem);
        if (status == ErrorCodes::NoSuchKey) {
            return MigrationSecondaryThrottleOptions(secondaryThrottle, boost::none);
        } else if (!status.isOK()) {
            return status;
        }

        if (secondaryThrottle != kOn) {
            return Status(ErrorCodes::UnsupportedFormat,
                          "Cannot specify write concern when secondaryThrottle is not set");
        }

        writeConcernBSON = writeConcernElem.Obj().getOwned();
    }

    invariant(writeConcernBSON.is_initialized());

    // Make sure the write concern parses correctly
    WriteConcernOptions writeConcern;
    Status status = writeConcern.parse(*writeConcernBSON);
    if (!status.isOK()) {
        return status;
    }

    return MigrationSecondaryThrottleOptions(secondaryThrottle, std::move(writeConcernBSON));
}
    Status _extractRoleDataFromBSONArray(const BSONElement& rolesElement,
                                         const std::string& dbname,
                                         std::vector<User::RoleData> *parsedRoleData) {
        for (BSONObjIterator it(rolesElement.Obj()); it.more(); it.next()) {
            BSONElement element = *it;
            if (element.type() == String) {
                RoleName roleName(element.String(), dbname);
                parsedRoleData->push_back(User::RoleData(roleName, true, false));
           } else if (element.type() == Object) {
               // Check that the role object is valid
               V2UserDocumentParser parser;
               BSONObj roleObj = element.Obj();
               Status status = parser.checkValidRoleObject(roleObj, true);
               if (!status.isOK()) {
                   return status;
               }

               std::string roleName;
               std::string roleSource;
               bool hasRole;
               bool canDelegate;
               status = bsonExtractStringField(roleObj, "name", &roleName);
               if (!status.isOK()) {
                   return status;
               }
               status = bsonExtractStringField(roleObj, "source", &roleSource);
               if (!status.isOK()) {
                   return status;
               }
               status = bsonExtractBooleanField(roleObj, "hasRole", &hasRole);
               if (!status.isOK()) {
                   return status;
               }
               status = bsonExtractBooleanField(roleObj, "canDelegate", &canDelegate);
               if (!status.isOK()) {
                   return status;
               }

               parsedRoleData->push_back(User::RoleData(RoleName(roleName, roleSource),
                                                        hasRole,
                                                        canDelegate));
           } else {
               return Status(ErrorCodes::UnsupportedFormat,
                             "Values in 'roles' array must be sub-documents or strings");
           }
       }

       return Status::OK();
    }
StatusWith<MigrationSecondaryThrottleOptions>
MigrationSecondaryThrottleOptions::createFromBalancerConfig(const BSONObj& obj) {
    {
        bool isSecondaryThrottle;
        Status status =
            bsonExtractBooleanField(obj, kSecondaryThrottleMongos, &isSecondaryThrottle);
        if (status.isOK()) {
            return MigrationSecondaryThrottleOptions::create(isSecondaryThrottle ? kOn : kOff);
        } else if (status == ErrorCodes::NoSuchKey) {
            return MigrationSecondaryThrottleOptions::create(kDefault);
        } else if (status != ErrorCodes::TypeMismatch) {
            return status;
        }
    }

    // Try to load it as a BSON document
    BSONElement elem;
    Status status = bsonExtractTypedField(obj, kSecondaryThrottleMongos, BSONType::Object, &elem);

    WriteConcernOptions writeConcern;
    Status writeConcernParseStatus = writeConcern.parse(elem.Obj());
    if (!writeConcernParseStatus.isOK()) {
        return writeConcernParseStatus;
    }

    return MigrationSecondaryThrottleOptions::createWithWriteConcern(writeConcern);
}
Status ReplSetRequestVotesArgs::initialize(const BSONObj& argsObj) {
    Status status = bsonCheckOnlyHasFields("ReplSetRequestVotes", argsObj, kLegalArgsFieldNames);
    if (!status.isOK())
        return status;

    status = bsonExtractIntegerField(argsObj, kTermFieldName, &_term);
    if (!status.isOK())
        return status;

    status = bsonExtractIntegerField(argsObj, kCandidateIdFieldName, &_candidateId);
    if (!status.isOK())
        return status;

    status = bsonExtractIntegerField(argsObj, kConfigVersionFieldName, &_cfgver);
    if (!status.isOK())
        return status;

    status = bsonExtractStringField(argsObj, kSetNameFieldName, &_setName);
    if (!status.isOK())
        return status;

    status = bsonExtractBooleanField(argsObj, kDryRunFieldName, &_dryRun);
    if (!status.isOK())
        return status;

    status = bsonExtractOpTimeField(argsObj, kLastCommittedOpFieldName, &_lastCommittedOp);
    if (!status.isOK())
        return status;

    return Status::OK();
}
Пример #6
0
StatusWith<ChunkType> ChunkType::fromBSON(const BSONObj& source) {
    ChunkType chunk;

    {
        std::string chunkName;
        Status status = bsonExtractStringField(source, name.name(), &chunkName);
        if (!status.isOK())
            return status;
        chunk._name = chunkName;
    }

    {
        std::string chunkNS;
        Status status = bsonExtractStringField(source, ns.name(), &chunkNS);
        if (!status.isOK())
            return status;
        chunk._ns = chunkNS;
    }

    {
        auto chunkRangeStatus = ChunkRange::fromBSON(source);
        if (!chunkRangeStatus.isOK())
            return chunkRangeStatus.getStatus();

        const auto chunkRange = std::move(chunkRangeStatus.getValue());
        chunk._min = chunkRange.getMin().getOwned();
        chunk._max = chunkRange.getMax().getOwned();
    }

    {
        std::string chunkShard;
        Status status = bsonExtractStringField(source, shard.name(), &chunkShard);
        if (!status.isOK())
            return status;
        chunk._shard = chunkShard;
    }

    {
        bool chunkJumbo;
        Status status = bsonExtractBooleanField(source, jumbo.name(), &chunkJumbo);
        if (status.isOK()) {
            chunk._jumbo = chunkJumbo;
        } else if (status == ErrorCodes::NoSuchKey) {
            // Jumbo status is missing, so it will be presumed false
        } else {
            return status;
        }
    }

    // The format of chunk version encoding is { lastmod: <Major|Minor>, lastmodEpoch: OID }
    if (!ChunkVersion::canParseBSON(source, DEPRECATED_lastmod())) {
        return Status(ErrorCodes::BadValue,
                      str::stream() << "Unable to parse chunk version from " << source);
    }
    chunk._version = ChunkVersion::fromBSON(source, DEPRECATED_lastmod());

    return chunk;
}
Пример #7
0
StatusWith<AutoSplitSettingsType> AutoSplitSettingsType::fromBSON(const BSONObj& obj) {
    bool shouldAutoSplit;
    Status status = bsonExtractBooleanField(obj, kEnabled, &shouldAutoSplit);
    if (!status.isOK())
        return status;

    AutoSplitSettingsType settings;
    settings._shouldAutoSplit = shouldAutoSplit;

    return settings;
}
Пример #8
0
StatusWith<MongosType> MongosType::fromBSON(const BSONObj& source) {
    MongosType mt;

    {
        std::string mtName;
        Status status = bsonExtractStringField(source, name.name(), &mtName);
        if (!status.isOK())
            return status;
        mt._name = mtName;
    }

    {
        BSONElement mtPingElem;
        Status status = bsonExtractTypedField(source, ping.name(), BSONType::Date, &mtPingElem);
        if (!status.isOK())
            return status;
        mt._ping = mtPingElem.date();
    }

    {
        long long mtUptime;
        Status status = bsonExtractIntegerField(source, uptime.name(), &mtUptime);
        if (!status.isOK())
            return status;
        mt._uptime = mtUptime;
    }

    {
        bool mtWaiting;
        Status status = bsonExtractBooleanField(source, waiting.name(), &mtWaiting);
        if (!status.isOK())
            return status;
        mt._waiting = mtWaiting;
    }

    if (source.hasField(mongoVersion.name())) {
        std::string mtMongoVersion;
        Status status = bsonExtractStringField(source, mongoVersion.name(), &mtMongoVersion);
        if (!status.isOK())
            return status;
        mt._mongoVersion = mtMongoVersion;
    }

    if (source.hasField(configVersion.name())) {
        long long mtConfigVersion;
        Status status = bsonExtractIntegerField(source, configVersion.name(), &mtConfigVersion);
        if (!status.isOK())
            return status;
        mt._configVersion = mtConfigVersion;
    }

    return mt;
}
Пример #9
0
StatusWith<ChunkType> ChunkType::fromConfigBSON(const BSONObj& source) {
    ChunkType chunk;

    {
        std::string chunkNS;
        Status status = bsonExtractStringField(source, ns.name(), &chunkNS);
        if (!status.isOK())
            return status;
        chunk._nss = NamespaceString(chunkNS);
    }

    {
        auto chunkRangeStatus = ChunkRange::fromBSON(source);
        if (!chunkRangeStatus.isOK())
            return chunkRangeStatus.getStatus();

        const auto chunkRange = std::move(chunkRangeStatus.getValue());
        chunk._min = chunkRange.getMin().getOwned();
        chunk._max = chunkRange.getMax().getOwned();
    }

    {
        std::string chunkShard;
        Status status = bsonExtractStringField(source, shard.name(), &chunkShard);
        if (!status.isOK())
            return status;
        chunk._shard = chunkShard;
    }

    {
        bool chunkJumbo;
        Status status = bsonExtractBooleanField(source, jumbo.name(), &chunkJumbo);
        if (status.isOK()) {
            chunk._jumbo = chunkJumbo;
        } else if (status == ErrorCodes::NoSuchKey) {
            // Jumbo status is missing, so it will be presumed false
        } else {
            return status;
        }
    }

    {
        auto versionStatus = ChunkVersion::parseFromBSONForChunk(source);
        if (!versionStatus.isOK()) {
            return versionStatus.getStatus();
        }
        chunk._version = std::move(versionStatus.getValue());
    }

    return chunk;
}
    Status parseAndValidateInfoCommands(const BSONObj& cmdObj,
                                        const StringData& cmdName,
                                        const std::string& dbname,
                                        bool* parsedAnyDB,
                                        BSONElement* parsedNameFilter) {
        unordered_set<std::string> validFieldNames;
        validFieldNames.insert(cmdName.toString());
        validFieldNames.insert("anyDB");
        validFieldNames.insert("writeConcern");
        validFieldNames.insert("details");

        Status status = _checkNoExtraFields(cmdObj, cmdName, validFieldNames);
        if (!status.isOK()) {
            return status;
        }

        if (cmdObj[cmdName].type() != String && cmdObj[cmdName].type() != RegEx) {
            return Status(ErrorCodes::BadValue,
                          mongoutils::str::stream() << "Argument to \"" << cmdName <<
                                  "\"command must be either a string or a regex");
        }
        *parsedNameFilter = cmdObj[cmdName];


        bool anyDB = false;
        if (cmdObj.hasField("anyDB")) {
            if (dbname == "admin") {
                Status status = bsonExtractBooleanField(cmdObj, "anyDB", &anyDB);
                if (!status.isOK()) {
                    return status;
                }
            } else {
                return Status(ErrorCodes::BadValue,
                              mongoutils::str::stream() << "\"anyDB\" argument to \"" << cmdName <<
                                      "\"command is only valid when run on the \"admin\" database");
            }
        }
        *parsedAnyDB = anyDB;

        return Status::OK();
    }
    Status parseAndValidateUsersInfoCommand(const BSONObj& cmdObj,
                                            const std::string& dbname,
                                            bool* parsedAnyDB,
                                            BSONElement* parsedUsersFilter) {
        unordered_set<std::string> validFieldNames;
        validFieldNames.insert("usersInfo");
        validFieldNames.insert("anyDB");
        validFieldNames.insert("writeConcern");

        Status status = _checkNoExtraFields(cmdObj, "usersInfo", validFieldNames);
        if (!status.isOK()) {
            return status;
        }

        if (cmdObj["usersInfo"].type() != String && cmdObj["usersInfo"].type() != RegEx) {
            return Status(ErrorCodes::BadValue,
                          "Argument to userInfo command must be either a string or a regex");
        }
        *parsedUsersFilter = cmdObj["usersInfo"];


        bool anyDB = false;
        if (cmdObj.hasField("anyDB")) {
            if (dbname == "admin") {
                Status status = bsonExtractBooleanField(cmdObj, "anyDB", &anyDB);
                if (!status.isOK()) {
                    return status;
                }
            } else {
                return Status(ErrorCodes::BadValue,
                              "\"anyDB\" argument to usersInfo command is only valid when "
                                      "run on the \"admin\" database");
            }
        }
        *parsedAnyDB = anyDB;

        return Status::OK();
    }
Пример #12
0
StatusWith<SettingsType> SettingsType::fromBSON(const BSONObj& source) {
    SettingsType settings;

    {
        std::string settingsKey;
        Status status = bsonExtractStringField(source, key.name(), &settingsKey);
        if (!status.isOK())
            return status;
        settings._key = settingsKey;
    }

    if (settings._key == ChunkSizeDocKey) {
        long long settingsChunkSizeMB;
        Status status = bsonExtractIntegerField(source, chunkSizeMB.name(), &settingsChunkSizeMB);
        if (!status.isOK())
            return status;
        settings._chunkSizeMB = settingsChunkSizeMB;
    } else if (settings._key == BalancerDocKey) {
        {
            bool settingsBalancerStopped;
            Status status = bsonExtractBooleanFieldWithDefault(
                source, balancerStopped.name(), false, &settingsBalancerStopped);
            if (!status.isOK())
                return status;
            settings._balancerStopped = settingsBalancerStopped;
        }

        {
            BSONElement settingsBalancerActiveWindowElem;
            Status status = bsonExtractTypedField(
                source, balancerActiveWindow.name(), Object, &settingsBalancerActiveWindowElem);
            if (status != ErrorCodes::NoSuchKey) {
                if (!status.isOK())
                    return status;
                StatusWith<BoostTimePair> timePairResult =
                    settings._parseBalancingWindow(settingsBalancerActiveWindowElem.Obj());
                if (!timePairResult.isOK())
                    return timePairResult.getStatus();
                settings._balancerActiveWindow = timePairResult.getValue();
            }
        }

        {
            BSONElement settingsMigrationWriteConcernElem;
            Status status = bsonExtractTypedField(
                source, migrationWriteConcern.name(), Object, &settingsMigrationWriteConcernElem);
            if (status == ErrorCodes::TypeMismatch) {
                bool settingsSecondaryThrottle;
                status = bsonExtractBooleanFieldWithDefault(
                    source, deprecated_secondaryThrottle.name(), true, &settingsSecondaryThrottle);
                if (!status.isOK())
                    return status;
                settings._secondaryThrottle = settingsSecondaryThrottle;
            } else if (status != ErrorCodes::NoSuchKey) {
                if (!status.isOK())
                    return status;
                settings._migrationWriteConcern = WriteConcernOptions();
                status =
                    settings._migrationWriteConcern->parse(settingsMigrationWriteConcernElem.Obj());
                if (!status.isOK())
                    return status;
            }
        }

        {
            bool settingsWaitForDelete;
            Status status =
                bsonExtractBooleanField(source, waitForDelete.name(), &settingsWaitForDelete);
            if (status != ErrorCodes::NoSuchKey) {
                if (!status.isOK())
                    return status;
                settings._waitForDelete = settingsWaitForDelete;
            }
        }
    }

    return settings;
}
Пример #13
0
Status IsMasterResponse::initialize(const BSONObj& doc) {
    Status status = bsonExtractBooleanField(doc, kIsMasterFieldName, &_isMaster);
    if (!status.isOK()) {
        return status;
    }
    _isMasterSet = true;
    status = bsonExtractBooleanField(doc, kSecondaryFieldName, &_secondary);
    if (!status.isOK()) {
        return status;
    }
    _isSecondarySet = true;
    if (doc.hasField(kInfoFieldName)) {
        if (_isMaster || _secondary || !doc.hasField(kIsReplicaSetFieldName) ||
            !doc[kIsReplicaSetFieldName].booleanSafe()) {
            return Status(ErrorCodes::FailedToParse,
                          str::stream() << "Expected presence of \"" << kInfoFieldName
                                        << "\" field to indicate no valid config loaded, but other "
                                           "fields weren't as we expected");
        }
        _configSet = false;
        return Status::OK();
    } else {
        if (doc.hasField(kIsReplicaSetFieldName)) {
            return Status(ErrorCodes::FailedToParse,
                          str::stream() << "Found \"" << kIsReplicaSetFieldName
                                        << "\" field which should indicate that no valid config "
                                           "is loaded, but we didn't also have an \""
                                        << kInfoFieldName << "\" field as we expected");
        }
    }

    status = bsonExtractStringField(doc, kSetNameFieldName, &_setName);
    if (!status.isOK()) {
        return status;
    }
    _setNameSet = true;
    status = bsonExtractIntegerField(doc, kSetVersionFieldName, &_setVersion);
    if (!status.isOK()) {
        return status;
    }
    _setVersionSet = true;

    if (doc.hasField(kHostsFieldName)) {
        BSONElement hostsElement;
        status = bsonExtractTypedField(doc, kHostsFieldName, Array, &hostsElement);
        if (!status.isOK()) {
            return status;
        }
        for (BSONObjIterator it(hostsElement.Obj()); it.more();) {
            BSONElement hostElement = it.next();
            if (hostElement.type() != String) {
                return Status(ErrorCodes::TypeMismatch,
                              str::stream() << "Elements in \"" << kHostsFieldName
                                            << "\" array of isMaster response must be of type "
                                            << typeName(String) << " but found type "
                                            << typeName(hostElement.type()));
            }
            _hosts.push_back(HostAndPort(hostElement.String()));
        }
        _hostsSet = true;
    }

    if (doc.hasField(kPassivesFieldName)) {
        BSONElement passivesElement;
        status = bsonExtractTypedField(doc, kPassivesFieldName, Array, &passivesElement);
        if (!status.isOK()) {
            return status;
        }
        for (BSONObjIterator it(passivesElement.Obj()); it.more();) {
            BSONElement passiveElement = it.next();
            if (passiveElement.type() != String) {
                return Status(ErrorCodes::TypeMismatch,
                              str::stream() << "Elements in \"" << kPassivesFieldName
                                            << "\" array of isMaster response must be of type "
                                            << typeName(String) << " but found type "
                                            << typeName(passiveElement.type()));
            }
            _passives.push_back(HostAndPort(passiveElement.String()));
        }
        _passivesSet = true;
    }

    if (doc.hasField(kArbitersFieldName)) {
        BSONElement arbitersElement;
        status = bsonExtractTypedField(doc, kArbitersFieldName, Array, &arbitersElement);
        if (!status.isOK()) {
            return status;
        }
        for (BSONObjIterator it(arbitersElement.Obj()); it.more();) {
            BSONElement arbiterElement = it.next();
            if (arbiterElement.type() != String) {
                return Status(ErrorCodes::TypeMismatch,
                              str::stream() << "Elements in \"" << kArbitersFieldName
                                            << "\" array of isMaster response must be of type "
                                            << typeName(String) << " but found type "
                                            << typeName(arbiterElement.type()));
            }
            _arbiters.push_back(HostAndPort(arbiterElement.String()));
        }
        _arbitersSet = true;
    }

    if (doc.hasField(kPrimaryFieldName)) {
        std::string primaryString;
        status = bsonExtractStringField(doc, kPrimaryFieldName, &primaryString);
        if (!status.isOK()) {
            return status;
        }
        _primary = HostAndPort(primaryString);
        _primarySet = true;
    }

    if (doc.hasField(kArbiterOnlyFieldName)) {
        status = bsonExtractBooleanField(doc, kArbiterOnlyFieldName, &_arbiterOnly);
        if (!status.isOK()) {
            return status;
        }
        _arbiterOnlySet = true;
    }

    if (doc.hasField(kPassiveFieldName)) {
        status = bsonExtractBooleanField(doc, kPassiveFieldName, &_passive);
        if (!status.isOK()) {
            return status;
        }
        _passiveSet = true;
    }

    if (doc.hasField(kHiddenFieldName)) {
        status = bsonExtractBooleanField(doc, kHiddenFieldName, &_hidden);
        if (!status.isOK()) {
            return status;
        }
        _hiddenSet = true;
    }

    if (doc.hasField(kBuildIndexesFieldName)) {
        status = bsonExtractBooleanField(doc, kBuildIndexesFieldName, &_buildIndexes);
        if (!status.isOK()) {
            return status;
        }
        _buildIndexesSet = true;
    }

    if (doc.hasField(kSlaveDelayFieldName)) {
        long long slaveDelaySecs;
        status = bsonExtractIntegerField(doc, kSlaveDelayFieldName, &slaveDelaySecs);
        if (!status.isOK()) {
            return status;
        }
        _slaveDelaySet = true;
        _slaveDelay = Seconds(slaveDelaySecs);
    }

    if (doc.hasField(kTagsFieldName)) {
        BSONElement tagsElement;
        status = bsonExtractTypedField(doc, kTagsFieldName, Object, &tagsElement);
        if (!status.isOK()) {
            return status;
        }
        for (BSONObjIterator it(tagsElement.Obj()); it.more();) {
            BSONElement tagElement = it.next();
            if (tagElement.type() != String) {
                return Status(ErrorCodes::TypeMismatch,
                              str::stream() << "Elements in \"" << kTagsFieldName
                                            << "\" obj "
                                               "of isMaster response must be of type "
                                            << typeName(String) << " but found type "
                                            << typeName(tagsElement.type()));
            }
            _tags[tagElement.fieldNameStringData().toString()] = tagElement.String();
        }
        _tagsSet = true;
    }

    if (doc.hasField(kElectionIdFieldName)) {
        BSONElement electionIdElem;
        status = bsonExtractTypedField(doc, kElectionIdFieldName, jstOID, &electionIdElem);
        if (!status.isOK()) {
            return status;
        }
        _electionId = electionIdElem.OID();
    }

    std::string meString;
    status = bsonExtractStringField(doc, kMeFieldName, &meString);
    if (!status.isOK()) {
        return status;
    }
    _me = HostAndPort(meString);
    _meSet = true;

    return Status::OK();
}
Пример #14
0
StatusWith<ShardCollectionType> ShardCollectionType::fromBSON(const BSONObj& source) {
    NamespaceString uuid;
    {
        std::string ns;
        Status status = bsonExtractStringField(source, ShardCollectionType::uuid.name(), &ns);
        if (!status.isOK())
            return status;
        uuid = NamespaceString{ns};
    }

    NamespaceString nss;
    {
        std::string ns;
        Status status = bsonExtractStringField(source, ShardCollectionType::ns.name(), &ns);
        if (!status.isOK()) {
            return status;
        }
        nss = NamespaceString{ns};
    }

    OID epoch;
    {
        BSONElement oidElem;
        Status status = bsonExtractTypedField(
            source, ShardCollectionType::epoch.name(), BSONType::jstOID, &oidElem);
        if (!status.isOK())
            return status;
        epoch = oidElem.OID();
    }

    BSONElement collKeyPattern;
    Status status = bsonExtractTypedField(
        source, ShardCollectionType::keyPattern.name(), Object, &collKeyPattern);
    if (!status.isOK()) {
        return status;
    }
    BSONObj obj = collKeyPattern.Obj();
    if (obj.isEmpty()) {
        return Status(ErrorCodes::ShardKeyNotFound,
                      str::stream() << "Empty shard key. Failed to parse: " << source.toString());
    }
    KeyPattern pattern(obj.getOwned());

    BSONObj collation;
    {
        BSONElement defaultCollation;
        Status status = bsonExtractTypedField(
            source, ShardCollectionType::defaultCollation.name(), Object, &defaultCollation);
        if (status.isOK()) {
            BSONObj obj = defaultCollation.Obj();
            if (obj.isEmpty()) {
                return Status(ErrorCodes::BadValue, "empty defaultCollation");
            }

            collation = obj.getOwned();
        } else if (status != ErrorCodes::NoSuchKey) {
            return status;
        }
    }

    bool unique;
    {
        Status status =
            bsonExtractBooleanField(source, ShardCollectionType::unique.name(), &unique);
        if (!status.isOK()) {
            return status;
        }
    }

    ShardCollectionType shardCollectionType(uuid, nss, epoch, pattern, collation, unique);

    // Below are optional fields.

    bool refreshing;
    {
        Status status =
            bsonExtractBooleanField(source, ShardCollectionType::refreshing.name(), &refreshing);
        if (status.isOK()) {
            shardCollectionType.setRefreshing(refreshing);
        } else if (status == ErrorCodes::NoSuchKey) {
            // The field is not set yet, which is okay.
        } else {
            return status;
        }
    }

    long long refreshSequenceNumber;
    {
        Status status = bsonExtractIntegerField(
            source, ShardCollectionType::refreshSequenceNumber.name(), &refreshSequenceNumber);
        if (status.isOK()) {
            shardCollectionType.setRefreshSequenceNumber(refreshSequenceNumber);
        } else if (status == ErrorCodes::NoSuchKey) {
            // The field is not set yet, which is okay.
        } else {
            return status;
        }
    }

    return shardCollectionType;
}
StatusWith<MoveChunkRequest> MoveChunkRequest::createFromCommand(NamespaceString nss,
                                                                 const BSONObj& obj) {
    auto secondaryThrottleStatus = MigrationSecondaryThrottleOptions::createFromCommand(obj);
    if (!secondaryThrottleStatus.isOK()) {
        return secondaryThrottleStatus.getStatus();
    }

    auto rangeStatus = ChunkRange::fromBSON(obj);
    if (!rangeStatus.isOK()) {
        return rangeStatus.getStatus();
    }

    MoveChunkRequest request(std::move(nss),
                             std::move(rangeStatus.getValue()),
                             std::move(secondaryThrottleStatus.getValue()));

    {
        std::string shardStr;
        Status status = bsonExtractStringField(obj, kFromShardId, &shardStr);
        request._fromShardId = shardStr;
        if (!status.isOK()) {
            return status;
        }
    }

    {
        std::string shardStr;
        Status status = bsonExtractStringField(obj, kToShardId, &shardStr);
        request._toShardId = shardStr;
        if (!status.isOK()) {
            return status;
        }
    }

    {
        BSONElement epochElem;
        Status status = bsonExtractTypedField(obj, kEpoch, BSONType::jstOID, &epochElem);
        if (!status.isOK())
            return status;
        request._versionEpoch = epochElem.OID();
    }

    {
        Status status =
            bsonExtractBooleanFieldWithDefault(obj, kWaitForDelete, false, &request._waitForDelete);
        if (!status.isOK()) {
            return status;
        }
    }

    // Check for the deprecated name '_waitForDelete' if 'waitForDelete' was false.
    if (!request._waitForDelete) {
        Status status = bsonExtractBooleanFieldWithDefault(
            obj, kWaitForDeleteDeprecated, false, &request._waitForDelete);
        if (!status.isOK()) {
            return status;
        }
    }

    {
        long long maxChunkSizeBytes;
        Status status = bsonExtractIntegerField(obj, kMaxChunkSizeBytes, &maxChunkSizeBytes);
        if (!status.isOK()) {
            return status;
        }

        request._maxChunkSizeBytes = static_cast<int64_t>(maxChunkSizeBytes);
    }

    {  // TODO: delete this block in 3.8
        bool takeDistLock = false;
        Status status = bsonExtractBooleanField(obj, kTakeDistLock, &takeDistLock);
        if (status.isOK() && takeDistLock) {
            return Status{ErrorCodes::IncompatibleShardingConfigVersion,
                          str::stream()
                              << "Request received from an older, incompatible mongodb version"};
        }
    }

    return request;
}
StatusWith<ShardType> ShardingCatalogManager::_validateHostAsShard(
    OperationContext* opCtx,
    std::shared_ptr<RemoteCommandTargeter> targeter,
    const std::string* shardProposedName,
    const ConnectionString& connectionString) {
    auto swCommandResponse = _runCommandForAddShard(
        opCtx, targeter.get(), NamespaceString::kAdminDb, BSON("isMaster" << 1));
    if (swCommandResponse.getStatus() == ErrorCodes::IncompatibleServerVersion) {
        return swCommandResponse.getStatus().withReason(
            str::stream() << "Cannot add " << connectionString.toString()
                          << " as a shard because its binary version is not compatible with "
                             "the cluster's featureCompatibilityVersion.");
    } else if (!swCommandResponse.isOK()) {
        return swCommandResponse.getStatus();
    }

    // Check for a command response error
    auto resIsMasterStatus = std::move(swCommandResponse.getValue().commandStatus);
    if (!resIsMasterStatus.isOK()) {
        return resIsMasterStatus.withContext(str::stream()
                                             << "Error running isMaster against "
                                             << targeter->connectionString().toString());
    }

    auto resIsMaster = std::move(swCommandResponse.getValue().response);

    // Fail if the node being added is a mongos.
    const std::string msg = resIsMaster.getStringField("msg");
    if (msg == "isdbgrid") {
        return {ErrorCodes::IllegalOperation, "cannot add a mongos as a shard"};
    }

    // Extract the maxWireVersion so we can verify that the node being added has a binary version
    // greater than or equal to the cluster's featureCompatibilityVersion. We expect an incompatible
    // binary node to be unable to communicate, returning an IncompatibleServerVersion error,
    // because of our internal wire version protocol. So we can safely invariant here that the node
    // is compatible.
    long long maxWireVersion;
    Status status = bsonExtractIntegerField(resIsMaster, "maxWireVersion", &maxWireVersion);
    if (!status.isOK()) {
        return status.withContext(str::stream() << "isMaster returned invalid 'maxWireVersion' "
                                                << "field when attempting to add "
                                                << connectionString.toString()
                                                << " as a shard");
    }
    if (serverGlobalParams.featureCompatibility.getVersion() >
        ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo40) {
        // If the cluster's FCV is 4.2, or upgrading to / downgrading from, the node being added
        // must be a v4.2 binary.
        invariant(maxWireVersion == WireVersion::LATEST_WIRE_VERSION);
    } else {
        // If the cluster's FCV is 4.0, the node being added must be a v4.0 or v4.2 binary.
        invariant(serverGlobalParams.featureCompatibility.getVersion() ==
                  ServerGlobalParams::FeatureCompatibility::Version::kFullyDowngradedTo40);
        invariant(maxWireVersion >= WireVersion::LATEST_WIRE_VERSION - 1);
    }

    // Check whether there is a master. If there isn't, the replica set may not have been
    // initiated. If the connection is a standalone, it will return true for isMaster.
    bool isMaster;
    status = bsonExtractBooleanField(resIsMaster, "ismaster", &isMaster);
    if (!status.isOK()) {
        return status.withContext(str::stream() << "isMaster returned invalid 'ismaster' "
                                                << "field when attempting to add "
                                                << connectionString.toString()
                                                << " as a shard");
    }
    if (!isMaster) {
        return {ErrorCodes::NotMaster,
                str::stream()
                    << connectionString.toString()
                    << " does not have a master. If this is a replica set, ensure that it has a"
                    << " healthy primary and that the set has been properly initiated."};
    }

    const std::string providedSetName = connectionString.getSetName();
    const std::string foundSetName = resIsMaster["setName"].str();

    // Make sure the specified replica set name (if any) matches the actual shard's replica set
    if (providedSetName.empty() && !foundSetName.empty()) {
        return {ErrorCodes::OperationFailed,
                str::stream() << "host is part of set " << foundSetName << "; "
                              << "use replica set url format "
                              << "<setname>/<server1>,<server2>, ..."};
    }

    if (!providedSetName.empty() && foundSetName.empty()) {
        return {ErrorCodes::OperationFailed,
                str::stream() << "host did not return a set name; "
                              << "is the replica set still initializing? "
                              << resIsMaster};
    }

    // Make sure the set name specified in the connection string matches the one where its hosts
    // belong into
    if (!providedSetName.empty() && (providedSetName != foundSetName)) {
        return {ErrorCodes::OperationFailed,
                str::stream() << "the provided connection string (" << connectionString.toString()
                              << ") does not match the actual set name "
                              << foundSetName};
    }

    // Is it a config server?
    if (resIsMaster.hasField("configsvr")) {
        return {ErrorCodes::OperationFailed,
                str::stream() << "Cannot add " << connectionString.toString()
                              << " as a shard since it is a config server"};
    }

    // If the shard is part of a replica set, make sure all the hosts mentioned in the connection
    // string are part of the set. It is fine if not all members of the set are mentioned in the
    // connection string, though.
    if (!providedSetName.empty()) {
        std::set<std::string> hostSet;

        BSONObjIterator iter(resIsMaster["hosts"].Obj());
        while (iter.more()) {
            hostSet.insert(iter.next().String());  // host:port
        }

        if (resIsMaster["passives"].isABSONObj()) {
            BSONObjIterator piter(resIsMaster["passives"].Obj());
            while (piter.more()) {
                hostSet.insert(piter.next().String());  // host:port
            }
        }

        if (resIsMaster["arbiters"].isABSONObj()) {
            BSONObjIterator piter(resIsMaster["arbiters"].Obj());
            while (piter.more()) {
                hostSet.insert(piter.next().String());  // host:port
            }
        }

        for (const auto& hostEntry : connectionString.getServers()) {
            const auto& host = hostEntry.toString();  // host:port
            if (hostSet.find(host) == hostSet.end()) {
                return {ErrorCodes::OperationFailed,
                        str::stream() << "in seed list " << connectionString.toString() << ", host "
                                      << host
                                      << " does not belong to replica set "
                                      << foundSetName
                                      << "; found "
                                      << resIsMaster.toString()};
            }
        }
    }

    std::string actualShardName;

    if (shardProposedName) {
        actualShardName = *shardProposedName;
    } else if (!foundSetName.empty()) {
        // Default it to the name of the replica set
        actualShardName = foundSetName;
    }

    // Disallow adding shard replica set with name 'config'
    if (actualShardName == NamespaceString::kConfigDb) {
        return {ErrorCodes::BadValue, "use of shard replica set with name 'config' is not allowed"};
    }

    // Retrieve the most up to date connection string that we know from the replica set monitor (if
    // this is a replica set shard, otherwise it will be the same value as connectionString).
    ConnectionString actualShardConnStr = targeter->connectionString();

    ShardType shard;
    shard.setName(actualShardName);
    shard.setHost(actualShardConnStr.toString());
    shard.setState(ShardType::ShardState::kShardAware);

    return shard;
}
StatusWith<ShardType> ShardingCatalogManagerImpl::_validateHostAsShard(
    OperationContext* opCtx,
    std::shared_ptr<RemoteCommandTargeter> targeter,
    const std::string* shardProposedName,
    const ConnectionString& connectionString) {

    // Check if the node being added is a mongos or a version of mongod too old to speak the current
    // communication protocol.
    auto swCommandResponse =
        _runCommandForAddShard(opCtx, targeter.get(), "admin", BSON("isMaster" << 1));
    if (!swCommandResponse.isOK()) {
        if (swCommandResponse.getStatus() == ErrorCodes::RPCProtocolNegotiationFailed) {
            // Mongos to mongos commands are no longer supported in the wire protocol
            // (because mongos does not support OP_COMMAND), similarly for a new mongos
            // and an old mongod. So the call will fail in such cases.
            // TODO: If/When mongos ever supports opCommands, this logic will break because
            // cmdStatus will be OK.
            return {ErrorCodes::RPCProtocolNegotiationFailed,
                    str::stream() << targeter->connectionString().toString()
                                  << " does not recognize the RPC protocol being used. This is"
                                  << " likely because it contains a node that is a mongos or an old"
                                  << " version of mongod."};
        } else {
            return swCommandResponse.getStatus();
        }
    }

    // Check for a command response error
    auto resIsMasterStatus = std::move(swCommandResponse.getValue().commandStatus);
    if (!resIsMasterStatus.isOK()) {
        return {resIsMasterStatus.code(),
                str::stream() << "Error running isMaster against "
                              << targeter->connectionString().toString()
                              << ": "
                              << causedBy(resIsMasterStatus)};
    }

    auto resIsMaster = std::move(swCommandResponse.getValue().response);

    // Check that the node being added is a new enough version.
    // If we're running this code, that means the mongos that the addShard request originated from
    // must be at least version 3.4 (since 3.2 mongoses don't know about the _configsvrAddShard
    // command).  Since it is illegal to have v3.4 mongoses with v3.2 shards, we should reject
    // adding any shards that are not v3.4.  We can determine this by checking that the
    // maxWireVersion reported in isMaster is at least COMMANDS_ACCEPT_WRITE_CONCERN.
    // TODO(SERVER-25623): This approach won't work to prevent v3.6 mongoses from adding v3.4
    // shards, so we'll have to rethink this during the 3.5 development cycle.

    long long maxWireVersion;
    Status status = bsonExtractIntegerField(resIsMaster, "maxWireVersion", &maxWireVersion);
    if (!status.isOK()) {
        return Status(status.code(),
                      str::stream() << "isMaster returned invalid 'maxWireVersion' "
                                    << "field when attempting to add "
                                    << connectionString.toString()
                                    << " as a shard: "
                                    << status.reason());
    }
    if (maxWireVersion < WireVersion::COMMANDS_ACCEPT_WRITE_CONCERN) {
        return Status(ErrorCodes::IncompatibleServerVersion,
                      str::stream() << "Cannot add " << connectionString.toString()
                                    << " as a shard because we detected a mongod with server "
                                       "version older than 3.4.0.  It is invalid to add v3.2 and "
                                       "older shards through a v3.4 mongos.");
    }


    // Check whether there is a master. If there isn't, the replica set may not have been
    // initiated. If the connection is a standalone, it will return true for isMaster.
    bool isMaster;
    status = bsonExtractBooleanField(resIsMaster, "ismaster", &isMaster);
    if (!status.isOK()) {
        return Status(status.code(),
                      str::stream() << "isMaster returned invalid 'ismaster' "
                                    << "field when attempting to add "
                                    << connectionString.toString()
                                    << " as a shard: "
                                    << status.reason());
    }
    if (!isMaster) {
        return {ErrorCodes::NotMaster,
                str::stream()
                    << connectionString.toString()
                    << " does not have a master. If this is a replica set, ensure that it has a"
                    << " healthy primary and that the set has been properly initiated."};
    }

    const std::string providedSetName = connectionString.getSetName();
    const std::string foundSetName = resIsMaster["setName"].str();

    // Make sure the specified replica set name (if any) matches the actual shard's replica set
    if (providedSetName.empty() && !foundSetName.empty()) {
        return {ErrorCodes::OperationFailed,
                str::stream() << "host is part of set " << foundSetName << "; "
                              << "use replica set url format "
                              << "<setname>/<server1>,<server2>, ..."};
    }

    if (!providedSetName.empty() && foundSetName.empty()) {
        return {ErrorCodes::OperationFailed,
                str::stream() << "host did not return a set name; "
                              << "is the replica set still initializing? "
                              << resIsMaster};
    }

    // Make sure the set name specified in the connection string matches the one where its hosts
    // belong into
    if (!providedSetName.empty() && (providedSetName != foundSetName)) {
        return {ErrorCodes::OperationFailed,
                str::stream() << "the provided connection string (" << connectionString.toString()
                              << ") does not match the actual set name "
                              << foundSetName};
    }

    // Is it a config server?
    if (resIsMaster.hasField("configsvr")) {
        return {ErrorCodes::OperationFailed,
                str::stream() << "Cannot add " << connectionString.toString()
                              << " as a shard since it is a config server"};
    }

    // If the shard is part of a replica set, make sure all the hosts mentioned in the connection
    // string are part of the set. It is fine if not all members of the set are mentioned in the
    // connection string, though.
    if (!providedSetName.empty()) {
        std::set<std::string> hostSet;

        BSONObjIterator iter(resIsMaster["hosts"].Obj());
        while (iter.more()) {
            hostSet.insert(iter.next().String());  // host:port
        }

        if (resIsMaster["passives"].isABSONObj()) {
            BSONObjIterator piter(resIsMaster["passives"].Obj());
            while (piter.more()) {
                hostSet.insert(piter.next().String());  // host:port
            }
        }

        if (resIsMaster["arbiters"].isABSONObj()) {
            BSONObjIterator piter(resIsMaster["arbiters"].Obj());
            while (piter.more()) {
                hostSet.insert(piter.next().String());  // host:port
            }
        }

        for (const auto& hostEntry : connectionString.getServers()) {
            const auto& host = hostEntry.toString();  // host:port
            if (hostSet.find(host) == hostSet.end()) {
                return {ErrorCodes::OperationFailed,
                        str::stream() << "in seed list " << connectionString.toString() << ", host "
                                      << host
                                      << " does not belong to replica set "
                                      << foundSetName
                                      << "; found "
                                      << resIsMaster.toString()};
            }
        }
    }

    std::string actualShardName;

    if (shardProposedName) {
        actualShardName = *shardProposedName;
    } else if (!foundSetName.empty()) {
        // Default it to the name of the replica set
        actualShardName = foundSetName;
    }

    // Disallow adding shard replica set with name 'config'
    if (actualShardName == NamespaceString::kConfigDb) {
        return {ErrorCodes::BadValue, "use of shard replica set with name 'config' is not allowed"};
    }

    // Retrieve the most up to date connection string that we know from the replica set monitor (if
    // this is a replica set shard, otherwise it will be the same value as connectionString).
    ConnectionString actualShardConnStr = targeter->connectionString();

    ShardType shard;
    shard.setName(actualShardName);
    shard.setHost(actualShardConnStr.toString());
    shard.setState(ShardType::ShardState::kShardAware);

    return shard;
}
Пример #18
0
StatusWith<MongosType> MongosType::fromBSON(const BSONObj& source) {
    MongosType mt;

    {
        std::string mtName;
        Status status = bsonExtractStringField(source, name.name(), &mtName);
        if (!status.isOK())
            return status;
        mt._name = mtName;
    }

    {
        BSONElement mtPingElem;
        Status status = bsonExtractTypedField(source, ping.name(), BSONType::Date, &mtPingElem);
        if (!status.isOK())
            return status;
        mt._ping = mtPingElem.date();
    }

    {
        long long mtUptime;
        Status status = bsonExtractIntegerField(source, uptime.name(), &mtUptime);
        if (!status.isOK())
            return status;
        mt._uptime = mtUptime;
    }

    {
        bool mtWaiting;
        Status status = bsonExtractBooleanField(source, waiting.name(), &mtWaiting);
        if (!status.isOK())
            return status;
        mt._waiting = mtWaiting;
    }

    if (source.hasField(mongoVersion.name())) {
        std::string mtMongoVersion;
        Status status = bsonExtractStringField(source, mongoVersion.name(), &mtMongoVersion);
        if (!status.isOK())
            return status;
        mt._mongoVersion = mtMongoVersion;
    }

    if (source.hasField(configVersion.name())) {
        long long mtConfigVersion;
        Status status = bsonExtractIntegerField(source, configVersion.name(), &mtConfigVersion);
        if (!status.isOK())
            return status;
        mt._configVersion = mtConfigVersion;
    }

    if (source.hasField(advisoryHostFQDNs.name())) {
        mt._advisoryHostFQDNs = std::vector<std::string>();
        BSONElement array;
        Status status = bsonExtractTypedField(source, advisoryHostFQDNs.name(), Array, &array);
        if (!status.isOK())
            return status;

        BSONObjIterator it(array.Obj());
        while (it.more()) {
            BSONElement arrayElement = it.next();
            if (arrayElement.type() != String) {
                return Status(ErrorCodes::TypeMismatch,
                              str::stream() << "Elements in \"" << advisoryHostFQDNs.name()
                                            << "\" array must be strings but found "
                                            << typeName(arrayElement.type()));
            }
            mt._advisoryHostFQDNs->push_back(arrayElement.String());
        }
    }

    return mt;
}
Пример #19
0
StatusWith<TextMatchExpressionBase::TextParams>
ExtensionsCallback::extractTextMatchExpressionParams(BSONElement text) {
    TextMatchExpressionBase::TextParams params;
    if (text.type() != Object) {
        return {ErrorCodes::BadValue, "$text expects an object"};
    }
    BSONObj queryObj = text.Obj();

    //
    // Parse required fields.
    //

    Status queryStatus = bsonExtractStringField(queryObj, "$search", &params.query);
    if (!queryStatus.isOK()) {
        return queryStatus;
    }

    //
    // Parse optional fields.
    //

    int expectedFieldCount = 1;

    Status languageStatus = bsonExtractStringField(queryObj, "$language", &params.language);
    if (languageStatus == ErrorCodes::TypeMismatch) {
        return languageStatus;
    } else if (languageStatus == ErrorCodes::NoSuchKey) {
        params.language = std::string();
    } else {
        invariantOK(languageStatus);
        expectedFieldCount++;
    }

    Status caseSensitiveStatus =
        bsonExtractBooleanField(queryObj, "$caseSensitive", &params.caseSensitive);
    if (caseSensitiveStatus == ErrorCodes::TypeMismatch) {
        return caseSensitiveStatus;
    } else if (caseSensitiveStatus == ErrorCodes::NoSuchKey) {
        params.caseSensitive = TextMatchExpressionBase::kCaseSensitiveDefault;
    } else {
        invariantOK(caseSensitiveStatus);
        expectedFieldCount++;
    }

    Status diacriticSensitiveStatus =
        bsonExtractBooleanField(queryObj, "$diacriticSensitive", &params.diacriticSensitive);
    if (diacriticSensitiveStatus == ErrorCodes::TypeMismatch) {
        return diacriticSensitiveStatus;
    } else if (diacriticSensitiveStatus == ErrorCodes::NoSuchKey) {
        params.diacriticSensitive = TextMatchExpressionBase::kDiacriticSensitiveDefault;
    } else {
        invariantOK(diacriticSensitiveStatus);
        expectedFieldCount++;
    }

    if (queryObj.nFields() != expectedFieldCount) {
        return {ErrorCodes::BadValue, "extra fields in $text"};
    }

    return {std::move(params)};
}
StatusWith<ShardType> ShardingCatalogManagerImpl::_validateHostAsShard(
    OperationContext* txn,
    std::shared_ptr<RemoteCommandTargeter> targeter,
    const std::string* shardProposedName,
    const ConnectionString& connectionString) {
    // Check whether any host in the connection is already part of the cluster.
    Grid::get(txn)->shardRegistry()->reload(txn);
    for (const auto& hostAndPort : connectionString.getServers()) {
        std::shared_ptr<Shard> shard;
        shard = Grid::get(txn)->shardRegistry()->getShardNoReload(hostAndPort.toString());
        if (shard) {
            return {ErrorCodes::OperationFailed,
                    str::stream() << "'" << hostAndPort.toString() << "' "
                    << "is already a member of the existing shard '"
                    << shard->getConnString().toString()
                    << "' ("
                    << shard->getId()
                    << ")."};
        }
    }

    // Check for mongos and older version mongod connections, and whether the hosts
    // can be found for the user specified replset.
    auto swCommandResponse =
        _runCommandForAddShard(txn, targeter.get(), "admin", BSON("isMaster" << 1));
    if (!swCommandResponse.isOK()) {
        if (swCommandResponse.getStatus() == ErrorCodes::RPCProtocolNegotiationFailed) {
            // Mongos to mongos commands are no longer supported in the wire protocol
            // (because mongos does not support OP_COMMAND), similarly for a new mongos
            // and an old mongod. So the call will fail in such cases.
            // TODO: If/When mongos ever supports opCommands, this logic will break because
            // cmdStatus will be OK.
            return {ErrorCodes::RPCProtocolNegotiationFailed,
                    str::stream() << targeter->connectionString().toString()
                    << " does not recognize the RPC protocol being used. This is"
                    << " likely because it contains a node that is a mongos or an old"
                    << " version of mongod."};
        } else {
            return swCommandResponse.getStatus();
        }
    }

    // Check for a command response error
    auto resIsMasterStatus = std::move(swCommandResponse.getValue().commandStatus);
    if (!resIsMasterStatus.isOK()) {
        return {resIsMasterStatus.code(),
                str::stream() << "Error running isMaster against "
                << targeter->connectionString().toString()
                << ": "
                << causedBy(resIsMasterStatus)};
    }

    auto resIsMaster = std::move(swCommandResponse.getValue().response);

    // Check whether there is a master. If there isn't, the replica set may not have been
    // initiated. If the connection is a standalone, it will return true for isMaster.
    bool isMaster;
    Status status = bsonExtractBooleanField(resIsMaster, "ismaster", &isMaster);
    if (!status.isOK()) {
        return Status(status.code(),
                      str::stream() << "isMaster returned invalid 'ismaster' "
                      << "field when attempting to add "
                      << connectionString.toString()
                      << " as a shard: "
                      << status.reason());
    }
    if (!isMaster) {
        return {ErrorCodes::NotMaster,
                str::stream()
                << connectionString.toString()
                << " does not have a master. If this is a replica set, ensure that it has a"
                << " healthy primary and that the set has been properly initiated."};
    }

    const string providedSetName = connectionString.getSetName();
    const string foundSetName = resIsMaster["setName"].str();

    // Make sure the specified replica set name (if any) matches the actual shard's replica set
    if (providedSetName.empty() && !foundSetName.empty()) {
        return {ErrorCodes::OperationFailed,
                str::stream() << "host is part of set " << foundSetName << "; "
                << "use replica set url format "
                << "<setname>/<server1>,<server2>, ..."};
    }

    if (!providedSetName.empty() && foundSetName.empty()) {
        return {ErrorCodes::OperationFailed,
                str::stream() << "host did not return a set name; "
                << "is the replica set still initializing? "
                << resIsMaster};
    }

    // Make sure the set name specified in the connection string matches the one where its hosts
    // belong into
    if (!providedSetName.empty() && (providedSetName != foundSetName)) {
        return {ErrorCodes::OperationFailed,
                str::stream() << "the provided connection string (" << connectionString.toString()
                << ") does not match the actual set name "
                << foundSetName};
    }

    // Is it a config server?
    if (resIsMaster.hasField("configsvr")) {
        return {ErrorCodes::OperationFailed,
                str::stream() << "Cannot add " << connectionString.toString()
                << " as a shard since it is a config server"};
    }

    // If the shard is part of a replica set, make sure all the hosts mentioned in the connection
    // string are part of the set. It is fine if not all members of the set are mentioned in the
    // connection string, though.
    if (!providedSetName.empty()) {
        std::set<string> hostSet;

        BSONObjIterator iter(resIsMaster["hosts"].Obj());
        while (iter.more()) {
            hostSet.insert(iter.next().String());  // host:port
        }

        if (resIsMaster["passives"].isABSONObj()) {
            BSONObjIterator piter(resIsMaster["passives"].Obj());
            while (piter.more()) {
                hostSet.insert(piter.next().String());  // host:port
            }
        }

        if (resIsMaster["arbiters"].isABSONObj()) {
            BSONObjIterator piter(resIsMaster["arbiters"].Obj());
            while (piter.more()) {
                hostSet.insert(piter.next().String());  // host:port
            }
        }

        vector<HostAndPort> hosts = connectionString.getServers();
        for (size_t i = 0; i < hosts.size(); i++) {
            const string host = hosts[i].toString();  // host:port
            if (hostSet.find(host) == hostSet.end()) {
                return {ErrorCodes::OperationFailed,
                        str::stream() << "in seed list " << connectionString.toString() << ", host "
                        << host
                        << " does not belong to replica set "
                        << foundSetName
                        << "; found "
                        << resIsMaster.toString()};
            }
        }
    }

    string actualShardName;

    if (shardProposedName) {
        actualShardName = *shardProposedName;
    } else if (!foundSetName.empty()) {
        // Default it to the name of the replica set
        actualShardName = foundSetName;
    }

    // Disallow adding shard replica set with name 'config'
    if (actualShardName == "config") {
        return {ErrorCodes::BadValue, "use of shard replica set with name 'config' is not allowed"};
    }

    // Retrieve the most up to date connection string that we know from the replica set monitor (if
    // this is a replica set shard, otherwise it will be the same value as connectionString).
    ConnectionString actualShardConnStr = targeter->connectionString();

    ShardType shard;
    shard.setName(actualShardName);
    shard.setHost(actualShardConnStr.toString());

    return shard;
}
Пример #21
0
        Status _parseAndValidateInput(BSONObj cmdObj, CreateUserArgs* parsedArgs) const {
            unordered_set<std::string> validFieldNames;
            validFieldNames.insert("createUser");
            validFieldNames.insert("user");
            validFieldNames.insert("pwd");
            validFieldNames.insert("userSource");
            validFieldNames.insert("roles");
            validFieldNames.insert("readOnly");
            validFieldNames.insert("otherDBRoles");


            // Iterate through all fields in command object and make sure there are no unexpected
            // ones.
            for (BSONObjIterator iter(cmdObj); iter.more(); iter.next()) {
                StringData fieldName = (*iter).fieldNameStringData();
                if (!validFieldNames.count(fieldName.toString())) {
                    return Status(ErrorCodes::BadValue,
                                  mongoutils::str::stream() << "\"" << fieldName << "\" is not "
                                          "a valid argument to createUser");
                }
            }

            Status status = bsonExtractStringField(cmdObj, "user", &parsedArgs->userName);
            if (!status.isOK()) {
                return status;
            }

            if (cmdObj.hasField("pwd")) {
                parsedArgs->hasPassword = true;
                status = bsonExtractStringField(cmdObj, "pwd", &parsedArgs->clearTextPassword);
                if (!status.isOK()) {
                    return status;
                }
            }


            if (cmdObj.hasField("userSource")) {
                parsedArgs->hasUserSource = true;
                status = bsonExtractStringField(cmdObj, "userSource", &parsedArgs->userSource);
                if (!status.isOK()) {
                    return status;
                }
            }

            if (cmdObj.hasField("readOnly")) {
                parsedArgs->hasReadOnly = true;
                status = bsonExtractBooleanField(cmdObj, "readOnly", &parsedArgs->readOnly);
                if (!status.isOK()) {
                    return status;
                }
            }

            if (cmdObj.hasField("extraData")) {
                parsedArgs->hasExtraData = true;
                BSONElement element;
                status = bsonExtractTypedField(cmdObj, "extraData", Object, &element);
                if (!status.isOK()) {
                    return status;
                }
                parsedArgs->extraData = element.Obj();
            }

            if (cmdObj.hasField("roles")) {
                parsedArgs->hasRoles = true;
                BSONElement element;
                status = bsonExtractTypedField(cmdObj, "roles", Array, &element);
                if (!status.isOK()) {
                    return status;
                }
                parsedArgs->roles = BSONArray(element.Obj());
            }

            if (cmdObj.hasField("otherDBRoles")) {
                parsedArgs->hasOtherDBRoles = true;
                BSONElement element;
                status = bsonExtractTypedField(cmdObj, "otherDBRoles", Object, &element);
                if (!status.isOK()) {
                    return status;
                }
                parsedArgs->otherDBRoles = element.Obj();
            }

            if (parsedArgs->hasPassword && parsedArgs->hasUserSource) {
                return Status(ErrorCodes::BadValue,
                              "User objects can't have both 'pwd' and 'userSource'");
            }
            if (!parsedArgs->hasPassword && !parsedArgs->hasUserSource) {
                return Status(ErrorCodes::BadValue,
                              "User objects must have one of 'pwd' and 'userSource'");
            }
            if (parsedArgs->hasRoles && parsedArgs->hasReadOnly) {
                return Status(ErrorCodes::BadValue,
                              "User objects can't have both 'roles' and 'readOnly'");
            }

            return Status::OK();
        }
Пример #22
0
StatusWith<ChunkType> ChunkType::fromBSON(const BSONObj& source) {
    ChunkType chunk;

    {
        std::string chunkName;
        Status status = bsonExtractStringField(source, name.name(), &chunkName);
        if (!status.isOK())
            return status;
        chunk._name = chunkName;
    }

    {
        std::string chunkNS;
        Status status = bsonExtractStringField(source, ns.name(), &chunkNS);
        if (!status.isOK())
            return status;
        chunk._ns = chunkNS;
    }

    {
        BSONElement chunkMinElement;
        Status status = bsonExtractTypedField(source, min.name(), Object, &chunkMinElement);
        if (!status.isOK())
            return status;
        chunk._min = chunkMinElement.Obj().getOwned();
    }

    {
        BSONElement chunkMaxElement;
        Status status = bsonExtractTypedField(source, max.name(), Object, &chunkMaxElement);
        if (!status.isOK())
            return status;
        chunk._max = chunkMaxElement.Obj().getOwned();
    }

    {
        std::string chunkShard;
        Status status = bsonExtractStringField(source, shard.name(), &chunkShard);
        if (!status.isOK())
            return status;
        chunk._shard = chunkShard;
    }

    {
        bool chunkJumbo;
        Status status = bsonExtractBooleanField(source, jumbo.name(), &chunkJumbo);
        if (status.isOK()) {
            chunk._jumbo = chunkJumbo;
        } else if (status == ErrorCodes::NoSuchKey) {
            // Jumbo status is missing, so it will be presumed false
        } else {
            return status;
        }
    }

    //
    // ChunkVersion backward compatibility logic contained in ChunkVersion
    //

    // ChunkVersion is currently encoded as { 'version': [<TS>,<OID>] }

    if (ChunkVersion::canParseBSON(source, version())) {
        chunk._version = ChunkVersion::fromBSON(source, version());
    } else if (ChunkVersion::canParseBSON(source, DEPRECATED_lastmod())) {
        chunk._version = ChunkVersion::fromBSON(source, DEPRECATED_lastmod());
    }

    return chunk;
}
Пример #23
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 = bsonExtractBooleanField(source, draining.name(), &isShardDraining);
        if (status.isOK()) {
            shard._draining = isShardDraining;
        } else if (status == ErrorCodes::NoSuchKey) {
            // draining field can be mssing in which case it is presumed false
        } else {
            return status;
        }
    }

    {
        long long shardMaxSizeMB;
        // maxSizeMB == 0 means there's no limitation to space usage.
        Status status = bsonExtractIntegerField(source, maxSizeMB.name(), &shardMaxSizeMB);
        if (status.isOK()) {
            shard._maxSizeMB = shardMaxSizeMB;
        } else if (status == ErrorCodes::NoSuchKey) {
            // maxSizeMB field can be missing in which case it is presumed false
        } else {
            return status;
        }
    }

    if (source.hasField(tags.name())) {
        shard._tags = std::vector<std::string>();
        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 ReplSetHeartbeatResponseV1::initialize(const BSONObj& doc) {
    Status status = bsonCheckOnlyHasFields("ReplSetHeartbeatResponse",
                                           doc,
                                           kLegalHeartbeatFieldNames);
    if (!status.isOK())
        return status;

    status = bsonExtractBooleanField(doc, kIsReplSetFieldName, &_isReplSet);
    if (!status.isOK())
        return status;

    status = bsonExtractStringField(doc, kReplSetFieldName, &_setName);
    if (!status.isOK())
        return status;

    long long stateInt;
    status = bsonExtractIntegerField(doc, kMemberStateFieldName, &stateInt);
    if (!status.isOK())
        return status;
    if (stateInt < 0 || stateInt > MemberState::RS_MAX) {
        return Status(ErrorCodes::BadValue, str::stream() << "Value for \"" <<
                      kMemberStateFieldName << "\" in response to replSetHeartbeat is "
                      "out of range; legal values are non-negative and no more than " <<
                      MemberState::RS_MAX);
    }
    _state = MemberState(static_cast<int>(stateInt));

    // extracting the lastCommittedOp is a bit of a process
    BSONObj lastOpTime = doc[kLastOpTimeFieldName].Obj();
    Timestamp ts;
    status = bsonExtractTimestampField(lastOpTime, kOpTimeFieldName, &ts);
    if (!status.isOK())
        return status;
    long long term;
    status = bsonExtractIntegerField(lastOpTime, kTermFieldName, &term);
    if (!status.isOK())
        return status;
    _lastOpTime = OpTime(lastOpTime[kOpTimeFieldName].timestamp(),
                         lastOpTime[kTermFieldName].Long());


    status = bsonExtractStringField(doc, kSyncSourceFieldName, &_syncingTo);
    if (!status.isOK())
        return status;

    status = bsonExtractIntegerField(doc, kConfigVersionFieldName, &_configVersion);
    if (!status.isOK())
        return status;

    status = bsonExtractIntegerField(doc, kPrimaryIdFieldName, &_primaryId);
    if (!status.isOK())
        return status;

    status = bsonExtractIntegerField(doc, kTermFieldName, &_term);
    if (!status.isOK())
        return status;

    const BSONElement hasDataElement = doc[kHasDataFieldName];
    _hasDataSet = !hasDataElement.eoo();
    _hasData = hasDataElement.trueValue();

    const BSONElement rsConfigElement = doc[kConfigFieldName];
    if (rsConfigElement.eoo()) {
        _configSet = false;
        _config = ReplicaSetConfig();
        return Status::OK();
    }
    else if (rsConfigElement.type() != Object) {
        return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" <<
                      kConfigFieldName << "\" in response to replSetHeartbeat to have type "
                      "Object, but found " << typeName(rsConfigElement.type()));
    }
    _configSet = true;
    return _config.initialize(rsConfigElement.Obj());
}