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<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);
}
StatusWith<LogicalTimeMetadata> LogicalTimeMetadata::readFromMetadata(
    const BSONElement& metadataElem) {
    if (metadataElem.eoo()) {
        return LogicalTimeMetadata();
    }

    const auto& obj = metadataElem.Obj();

    Timestamp ts;
    Status status = bsonExtractTimestampField(obj, kClusterTimeFieldName, &ts);
    if (!status.isOK()) {
        return status;
    }

    BSONElement signatureElem;
    status = bsonExtractTypedField(obj, kSignatureFieldName, Object, &signatureElem);
    if (!status.isOK()) {
        return status;
    }

    const auto& signatureObj = signatureElem.Obj();

    // Extract BinData type signature hash and construct a SHA1Block instance from it.
    BSONElement hashElem;
    status = bsonExtractTypedField(signatureObj, kSignatureHashFieldName, BinData, &hashElem);
    if (!status.isOK()) {
        return status;
    }

    int hashLength = 0;
    auto rawBinSignature = hashElem.binData(hashLength);
    BSONBinData proofBinData(rawBinSignature, hashLength, hashElem.binDataType());
    auto proofStatus = SHA1Block::fromBinData(proofBinData);

    if (!proofStatus.isOK()) {
        return proofStatus.getStatus();
    }

    long long keyId;
    status = bsonExtractIntegerField(signatureObj, kSignatureKeyIdFieldName, &keyId);
    if (!status.isOK()) {
        return status;
    }

    return LogicalTimeMetadata(
        SignedLogicalTime(LogicalTime(ts), std::move(proofStatus.getValue()), keyId));
}
Status bsonExtractOpTimeField(const BSONObj& object, StringData fieldName, repl::OpTime* out) {
    BSONElement element;
    Status status = bsonExtractTypedField(object, fieldName, Object, &element);
    if (!status.isOK())
        return status;

    BSONObj opTimeObj = element.Obj();
    Timestamp ts;
    status = bsonExtractTimestampField(opTimeObj, kTimestampFieldName, &ts);
    if (!status.isOK())
        return status;
    long long term;
    status = bsonExtractIntegerField(opTimeObj, kTermFieldName, &term);
    if (!status.isOK())
        return status;
    *out = repl::OpTime(ts, term);
    return Status::OK();
}
Status ReadAfterOpTimeArgs::initialize(const BSONObj& cmdObj) {
    auto afterElem = cmdObj[ReadAfterOpTimeArgs::kRootFieldName];

    if (afterElem.eoo()) {
        return Status::OK();
    }

    if (!afterElem.isABSONObj()) {
        return Status(ErrorCodes::FailedToParse, "'after' field should be an object");
    }

    BSONObj readAfterObj = afterElem.Obj();
    BSONElement opTimeElem;
    auto opTimeStatus = bsonExtractTypedField(
        readAfterObj, ReadAfterOpTimeArgs::kOpTimeFieldName, Object, &opTimeElem);

    if (!opTimeStatus.isOK()) {
        return opTimeStatus;
    }

    BSONObj opTimeObj = opTimeElem.Obj();
    BSONElement timestampElem;

    Timestamp timestamp;
    auto timestampStatus = bsonExtractTimestampField(
        opTimeObj, ReadAfterOpTimeArgs::kOpTimestampFieldName, &timestamp);

    if (!timestampStatus.isOK()) {
        return timestampStatus;
    }

    long long termNumber;
    auto termStatus =
        bsonExtractIntegerField(opTimeObj, ReadAfterOpTimeArgs::kOpTermFieldName, &termNumber);

    if (!termStatus.isOK()) {
        return termStatus;
    }

    _opTime = OpTime(timestamp, termNumber);

    return Status::OK();
}
    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;

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

        return Status::OK();
    }
Status ReadConcernArgs::initialize(const BSONElement& readConcernElem) {
    invariant(isEmpty());  // only legal to call on uninitialized object.

    if (readConcernElem.eoo()) {
        return Status::OK();
    }

    dassert(readConcernElem.fieldNameStringData() == kReadConcernFieldName);

    if (readConcernElem.type() != Object) {
        return Status(ErrorCodes::FailedToParse,
                      str::stream() << kReadConcernFieldName << " field should be an object");
    }

    BSONObj readConcernObj = readConcernElem.Obj();
    for (auto&& field : readConcernObj) {
        auto fieldName = field.fieldNameStringData();
        if (fieldName == kAfterOpTimeFieldName) {
            OpTime opTime;
            // TODO pass field in rather than scanning again.
            auto opTimeStatus =
                bsonExtractOpTimeField(readConcernObj, kAfterOpTimeFieldName, &opTime);
            if (!opTimeStatus.isOK()) {
                return opTimeStatus;
            }
            _opTime = opTime;
        } else if (fieldName == kAfterClusterTimeFieldName) {
            Timestamp afterClusterTime;
            auto afterClusterTimeStatus = bsonExtractTimestampField(
                readConcernObj, kAfterClusterTimeFieldName, &afterClusterTime);
            if (!afterClusterTimeStatus.isOK()) {
                return afterClusterTimeStatus;
            }
            _afterClusterTime = LogicalTime(afterClusterTime);
        } else if (fieldName == kAtClusterTimeFieldName) {
            Timestamp atClusterTime;
            auto atClusterTimeStatus =
                bsonExtractTimestampField(readConcernObj, kAtClusterTimeFieldName, &atClusterTime);
            if (!atClusterTimeStatus.isOK()) {
                return atClusterTimeStatus;
            }
            _atClusterTime = LogicalTime(atClusterTime);
        } else if (fieldName == kLevelFieldName) {
            std::string levelString;
            // TODO pass field in rather than scanning again.
            auto readCommittedStatus =
                bsonExtractStringField(readConcernObj, kLevelFieldName, &levelString);

            if (!readCommittedStatus.isOK()) {
                return readCommittedStatus;
            }

            if (levelString == kLocalReadConcernStr) {
                _level = ReadConcernLevel::kLocalReadConcern;
            } else if (levelString == kMajorityReadConcernStr) {
                _level = ReadConcernLevel::kMajorityReadConcern;
            } else if (levelString == kLinearizableReadConcernStr) {
                _level = ReadConcernLevel::kLinearizableReadConcern;
            } else if (levelString == kAvailableReadConcernStr) {
                _level = ReadConcernLevel::kAvailableReadConcern;
            } else if (levelString == kSnapshotReadConcernStr) {
                _level = ReadConcernLevel::kSnapshotReadConcern;
            } else {
                return Status(ErrorCodes::FailedToParse,
                              str::stream() << kReadConcernFieldName << '.' << kLevelFieldName
                                            << " must be either 'local', 'majority', "
                                               "'linearizable', 'available', or 'snapshot'");
            }
            _originalLevel = _level;
        } else {
            return Status(ErrorCodes::InvalidOptions,
                          str::stream() << "Unrecognized option in " << kReadConcernFieldName
                                        << ": "
                                        << fieldName);
        }
    }

    if (_afterClusterTime && _opTime) {
        return Status(ErrorCodes::InvalidOptions,
                      str::stream() << "Can not specify both " << kAfterClusterTimeFieldName
                                    << " and "
                                    << kAfterOpTimeFieldName);
    }

    if (_afterClusterTime && _atClusterTime) {
        return Status(ErrorCodes::InvalidOptions,
                      str::stream() << "Can not specify both " << kAfterClusterTimeFieldName
                                    << " and "
                                    << kAtClusterTimeFieldName);
    }

    // Note: 'available' should not be used with after cluster time, as cluster time can wait for
    // replication whereas the premise of 'available' is to avoid waiting. 'linearizable' should not
    // be used with after cluster time, since linearizable reads are inherently causally consistent.
    if (_afterClusterTime && getLevel() != ReadConcernLevel::kMajorityReadConcern &&
        getLevel() != ReadConcernLevel::kLocalReadConcern &&
        getLevel() != ReadConcernLevel::kSnapshotReadConcern) {
        return Status(ErrorCodes::InvalidOptions,
                      str::stream() << kAfterClusterTimeFieldName << " field can be set only if "
                                    << kLevelFieldName
                                    << " is equal to "
                                    << kMajorityReadConcernStr
                                    << ", "
                                    << kLocalReadConcernStr
                                    << ", or "
                                    << kSnapshotReadConcernStr);
    }

    if (_opTime && getLevel() == ReadConcernLevel::kSnapshotReadConcern) {
        return Status(ErrorCodes::InvalidOptions,
                      str::stream() << kAfterOpTimeFieldName << " field cannot be set if "
                                    << kLevelFieldName
                                    << " is equal to "
                                    << kSnapshotReadConcernStr);
    }

    if (_atClusterTime && getLevel() != ReadConcernLevel::kSnapshotReadConcern) {
        return Status(ErrorCodes::InvalidOptions,
                      str::stream() << kAtClusterTimeFieldName << " field can be set only if "
                                    << kLevelFieldName
                                    << " is equal to "
                                    << kSnapshotReadConcernStr);
    }

    if (_afterClusterTime && _afterClusterTime == LogicalTime::kUninitialized) {
        return Status(ErrorCodes::InvalidOptions,
                      str::stream() << kAfterClusterTimeFieldName << " cannot be a null timestamp");
    }

    if (_atClusterTime && _atClusterTime == LogicalTime::kUninitialized) {
        return Status(ErrorCodes::InvalidOptions,
                      str::stream() << kAtClusterTimeFieldName << " cannot be a null timestamp");
    }

    return Status::OK();
}
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());
}
    Status ReplSetHeartbeatResponse::initialize(const BSONObj& doc, long long term) {

        // Old versions set this even though they returned not "ok"
        _mismatch = doc[kMismatchFieldName].trueValue();
        if (_mismatch)
            return Status(ErrorCodes::InconsistentReplicaSetNames,
                          "replica set name doesn't match.");

        // Old versions sometimes set the replica set name ("set") but ok:0
        const BSONElement replSetNameElement = doc[kReplSetFieldName];
        if (replSetNameElement.eoo()) {
            _setName.clear();
        }
        else if (replSetNameElement.type() != String) {
            return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" <<
                          kReplSetFieldName << "\" field in response to replSetHeartbeat to have "
                          "type String, but found " << typeName(replSetNameElement.type()));
        }
        else {
            _setName = replSetNameElement.String();
        }

        if (_setName.empty()  && !doc[kOkFieldName].trueValue()) {
            std::string errMsg = doc[kErrMsgFieldName].str();

            BSONElement errCodeElem = doc[kErrorCodeFieldName];
            if (errCodeElem.ok()) {
                if (!errCodeElem.isNumber())
                    return Status(ErrorCodes::BadValue, "Error code is not a number!");

                int errorCode = errCodeElem.numberInt();
                return Status(ErrorCodes::Error(errorCode), errMsg);
            }
            return Status(ErrorCodes::UnknownError, errMsg);
        }

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

        const BSONElement electionTimeElement = doc[kElectionTimeFieldName];
        if (electionTimeElement.eoo()) {
            _electionTimeSet = false;
        }
        else if (electionTimeElement.type() == bsonTimestamp) {
            _electionTimeSet = true;
            _electionTime = electionTimeElement.timestamp();
        }
        else if (electionTimeElement.type() == Date) {
            _electionTimeSet = true;
            _electionTime = Timestamp(electionTimeElement.date());
        }
        else {
            return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" <<
                          kElectionTimeFieldName << "\" field in response to replSetHeartbeat "
                          "command to have type Date or Timestamp, but found type " <<
                          typeName(electionTimeElement.type()));
        }

        const BSONElement timeElement = doc[kTimeFieldName];
        if (timeElement.eoo()) {
            _timeSet = false;
        }
        else if (timeElement.isNumber()) {
            _timeSet = true;
            _time = Seconds(timeElement.numberLong());
        }
        else {
            return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" <<
                          kTimeFieldName << "\" field in response to replSetHeartbeat "
                          "command to have a numeric type, but found type " <<
                          typeName(timeElement.type()));
        }

        _isReplSet = doc[kIsReplSetFieldName].trueValue();

        // In order to support both the 3.0(V0) and 3.2(V1) heartbeats we must parse the OpTime
        // field based on its type. If it is a Date, we parse it as the timestamp and use
        // initialize's term argument to complete the OpTime type. If it is an Object, then it's
        // V1 and we construct an OpTime out of its nested fields.
        const BSONElement opTimeElement = doc[kOpTimeFieldName];
        if (opTimeElement.eoo()) {
            _opTimeSet = false;
        }
        else if (opTimeElement.type() == bsonTimestamp) {
            _opTimeSet = true;
            _opTime = OpTime(opTimeElement.timestamp(), term);
        }
        else if (opTimeElement.type() == Date) {
            _opTimeSet = true;
            _opTime = OpTime(Timestamp(opTimeElement.date()), term);
        }
        else if (opTimeElement.type() == Object) {
            BSONObj opTime = opTimeElement.Obj();
            Timestamp ts;
            Status status = bsonExtractTimestampField(opTime, kTimestampFieldName, &ts);
            if (!status.isOK())
                return status;
            long long term;
            status = bsonExtractIntegerField(opTime, kTermFieldName, &term);
            if (!status.isOK())
                return status;

            _opTimeSet = true;
            _opTime = OpTime(ts, term);
            // since a v1 OpTime was in the response, the member must be part of a replset
            _isReplSet = true;
        }
        else {
            return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" <<
                          kOpTimeFieldName << "\" field in response to replSetHeartbeat "
                          "command to have type Date or Timestamp, but found type " <<
                          typeName(opTimeElement.type()));
        }

        const BSONElement electableElement = doc[kIsElectableFieldName];
        if (electableElement.eoo()) {
            _electableSet = false;
        }
        else {
            _electableSet = true;
            _electable = electableElement.trueValue();
        }

        const BSONElement memberStateElement = doc[kMemberStateFieldName];
        if (memberStateElement.eoo()) {
            _stateSet = false;
        }
        else if (memberStateElement.type() != NumberInt &&
                 memberStateElement.type() != NumberLong) {
            return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" <<
                          kMemberStateFieldName << "\" field in response to replSetHeartbeat "
                          "command to have type NumberInt or NumberLong, but found type " <<
                          typeName(memberStateElement.type()));
        }
        else {
            long long stateInt = memberStateElement.numberLong();
            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);
            }
            _stateSet = true;
            _state = MemberState(static_cast<int>(stateInt));
        }

        _stateDisagreement = doc[kHasStateDisagreementFieldName].trueValue();


        // Not required for the case of uninitialized members -- they have no config
        const BSONElement configVersionElement = doc[kConfigVersionFieldName];

        // If we have an optime then we must have a configVersion
        if (_opTimeSet && configVersionElement.eoo()) {
            return Status(ErrorCodes::NoSuchKey, str::stream() <<
                          "Response to replSetHeartbeat missing required \"" <<
                          kConfigVersionFieldName << "\" field even though initialized");
        }

        // If there is a "v" (config version) then it must be an int.
        if (!configVersionElement.eoo() && configVersionElement.type() != NumberInt) {
            return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" <<
                          kConfigVersionFieldName <<
                          "\" field in response to replSetHeartbeat to have "
                          "type NumberInt, but found " << typeName(configVersionElement.type()));
        }
        _configVersion = configVersionElement.numberInt();

        const BSONElement hbMsgElement = doc[kHbMessageFieldName];
        if (hbMsgElement.eoo()) {
            _hbmsg.clear();
        }
        else if (hbMsgElement.type() != String) {
            return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" <<
                          kHbMessageFieldName << "\" field in response to replSetHeartbeat to have "
                          "type String, but found " << typeName(hbMsgElement.type()));
        }
        else {
            _hbmsg = hbMsgElement.String();
        }

        const BSONElement syncingToElement = doc[kSyncSourceFieldName];
        if (syncingToElement.eoo()) {
            _syncingTo = HostAndPort();
        }
        else if (syncingToElement.type() != String) {
            return Status(ErrorCodes::TypeMismatch, str::stream() << "Expected \"" <<
                          kSyncSourceFieldName << "\" field in response to replSetHeartbeat to "
                          "have type String, but found " << typeName(syncingToElement.type()));
        }
        else {
            _syncingTo = HostAndPort(syncingToElement.String());
        }

        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());
    }