Status parseRolesInfoCommand(const BSONObj& cmdObj,
                                 const StringData& dbname,
                                 RolesInfoArgs* parsedArgs) {
        unordered_set<std::string> validFieldNames;
        validFieldNames.insert("rolesInfo");
        validFieldNames.insert("showPrivileges");
        validFieldNames.insert("showBuiltinRoles");

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

        if (cmdObj["rolesInfo"].numberInt() == 1) {
            parsedArgs->allForDB = true;
        } else if (cmdObj["rolesInfo"].type() == Array) {
            status = parseRoleNamesFromBSONArray(BSONArray(cmdObj["rolesInfo"].Obj()),
                                                 dbname,
                                                 &parsedArgs->roleNames);
            if (!status.isOK()) {
                return status;
            }
        } else {
            RoleName name;
            status = _parseNameFromBSONElement(cmdObj["rolesInfo"],
                                               dbname,
                                               AuthorizationManager::ROLE_NAME_FIELD_NAME,
                                               AuthorizationManager::ROLE_SOURCE_FIELD_NAME,
                                               &name);
            if (!status.isOK()) {
                return status;
            }
            parsedArgs->roleNames.push_back(name);
        }

        status = bsonExtractBooleanFieldWithDefault(cmdObj,
                                                    "showPrivileges",
                                                    false,
                                                    &parsedArgs->showPrivileges);
        if (!status.isOK()) {
            return status;
        }

        status = bsonExtractBooleanFieldWithDefault(cmdObj,
                                                    "showBuiltinRoles",
                                                    false,
                                                    &parsedArgs->showBuiltinRoles);
        if (!status.isOK()) {
            return status;
        }

        return Status::OK();
    }
Exemple #2
0
    bool run(OperationContext* opCtx,
             const std::string&,
             const BSONObj& cmdObj,
             BSONObjBuilder& result) override {
        if (cmdObj["forShell"].trueValue())
            LastError::get(opCtx->getClient()).disable();

        Status status = ReplicationCoordinator::get(opCtx)->checkReplEnabledForCommand(&result);
        if (!status.isOK())
            return CommandHelpers::appendCommandStatus(result, status);

        bool includeInitialSync = false;
        Status initialSyncStatus =
            bsonExtractBooleanFieldWithDefault(cmdObj, "initialSync", false, &includeInitialSync);
        if (!initialSyncStatus.isOK()) {
            return CommandHelpers::appendCommandStatus(result, initialSyncStatus);
        }

        auto responseStyle = ReplicationCoordinator::ReplSetGetStatusResponseStyle::kBasic;
        if (includeInitialSync) {
            responseStyle = ReplicationCoordinator::ReplSetGetStatusResponseStyle::kInitialSync;
        }
        status =
            ReplicationCoordinator::get(opCtx)->processReplSetGetStatus(&result, responseStyle);
        return CommandHelpers::appendCommandStatus(result, status);
    }
Exemple #3
0
Status doSaslStart(const Client* client,
                   SaslAuthenticationSession* session,
                   const std::string& db,
                   const BSONObj& cmdObj,
                   BSONObjBuilder* result) {
    bool autoAuthorize = false;
    Status status = bsonExtractBooleanFieldWithDefault(
        cmdObj, saslCommandAutoAuthorizeFieldName, autoAuthorizeDefault, &autoAuthorize);
    if (!status.isOK())
        return status;

    std::string mechanism;
    status = extractMechanism(cmdObj, &mechanism);
    if (!status.isOK())
        return status;

    if (!sequenceContains(saslGlobalParams.authenticationMechanisms, mechanism) &&
        mechanism != "SCRAM-SHA-1") {
        // Always allow SCRAM-SHA-1 to pass to the first sasl step since we need to
        // handle internal user authentication, SERVER-16534
        result->append(saslCommandMechanismListFieldName,
                       saslGlobalParams.authenticationMechanisms);
        return Status(ErrorCodes::BadValue,
                      mongoutils::str::stream() << "Unsupported mechanism " << mechanism);
    }

    status = session->start(
        db, mechanism, saslGlobalParams.serviceName, saslGlobalParams.hostName, 1, autoAuthorize);
    if (!status.isOK())
        return status;

    return doSaslStep(client, session, cmdObj, result);
}
Exemple #4
0
    StatusWith<ShardType> ShardType::fromBSON(const BSONObj& source) {
        ShardType shard;

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

        {
            std::string shardHost;
            Status status = bsonExtractStringField(source, host.name(), &shardHost);
            if (!status.isOK()) return status;
            shard._host = shardHost;
        }

        {
            bool isShardDraining;
            Status status = bsonExtractBooleanFieldWithDefault(source,
                                                               draining.name(),
                                                               false,
                                                               &isShardDraining);
            if (!status.isOK()) return status;
            shard._draining = isShardDraining;
        }

        {
            long long shardMaxSize;
            // maxSize == 0 means there's no limitation to space usage.
            Status status = bsonExtractIntegerFieldWithDefault(source,
                                                               maxSize.name(),
                                                               0,
                                                               &shardMaxSize);
            if (!status.isOK()) return status;
            shard._maxSize = shardMaxSize;
        }

        shard._tags = std::vector<std::string>();
        if (source.hasField(tags.name())) {
            BSONElement tagsElement;
            Status status = bsonExtractTypedField(source, tags.name(), Array, &tagsElement);
            if (!status.isOK()) return status;

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

        return shard;
    }
Exemple #5
0
StatusWith<MigrationType> MigrationType::fromBSON(const BSONObj& source) {
    MigrationType migrationType;

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

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

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

    {
        std::string migrationToShard;
        Status status = bsonExtractStringField(source, toShard.name(), &migrationToShard);
        if (!status.isOK())
            return status;
        migrationType._toShard = std::move(migrationToShard);
    }

    {
        std::string migrationFromShard;
        Status status = bsonExtractStringField(source, fromShard.name(), &migrationFromShard);
        if (!status.isOK())
            return status;
        migrationType._fromShard = std::move(migrationFromShard);
    }

    {
        auto chunkVersionStatus = ChunkVersion::parseWithField(source, kChunkVersion);
        if (!chunkVersionStatus.isOK())
            return chunkVersionStatus.getStatus();
        migrationType._chunkVersion = chunkVersionStatus.getValue();
    }

    {
        bool waitForDeleteVal{false};
        Status status = bsonExtractBooleanFieldWithDefault(
            source, waitForDelete.name(), false, &waitForDeleteVal);
        if (!status.isOK())
            return status;
        migrationType._waitForDelete = waitForDeleteVal;
    }

    return migrationType;
}
Status ReplSetHeartbeatArgs::initialize(const BSONObj& argsObj) {
    Status status =
        bsonCheckOnlyHasFields("ReplSetHeartbeatArgs", argsObj, kLegalHeartbeatFieldNames);
    if (!status.isOK())
        return status;

    status = bsonExtractBooleanFieldWithDefault(argsObj, kCheckEmptyFieldName, false, &_checkEmpty);
    if (!status.isOK())
        return status;
    _hasCheckEmpty = true;

    status = bsonExtractIntegerField(argsObj, kProtocolVersionFieldName, &_protocolVersion);
    if (!status.isOK())
        return status;
    _hasProtocolVersion = true;

    status = bsonExtractIntegerField(argsObj, kConfigVersionFieldName, &_configVersion);
    if (!status.isOK())
        return status;
    _hasConfigVersion = true;

    status = bsonExtractIntegerFieldWithDefault(argsObj, kSenderIdFieldName, -1, &_senderId);
    if (!status.isOK())
        return status;
    _hasSenderId = true;

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

    std::string hostAndPortString;
    status =
        bsonExtractStringFieldWithDefault(argsObj, kSenderHostFieldName, "", &hostAndPortString);
    if (!status.isOK())
        return status;

    if (!hostAndPortString.empty()) {
        status = _senderHost.initialize(hostAndPortString);
        if (!status.isOK())
            return status;
        _hasSenderHost = true;
    }

    return Status::OK();
}
void MigrationManager::_checkMigrationCallback(
    const executor::TaskExecutor::RemoteCommandCallbackArgs& callbackArgs,
    OperationContext* txn,
    Migration* migration,
    MigrationStatuses* migrationStatuses) {
    const auto& remoteCommandResponseWithStatus = callbackArgs.response;

    if (!remoteCommandResponseWithStatus.isOK()) {
        stdx::lock_guard<stdx::mutex> lk(_mutex);
        migrationStatuses->insert(
            MigrationStatuses::value_type(migration->chunkInfo.migrateInfo.getName(),
                                          std::move(remoteCommandResponseWithStatus.getStatus())));
        return;
    }

    const auto& remoteCommandResponse = callbackArgs.response.getValue();

    Status commandStatus = getStatusFromCommandResult(remoteCommandResponse.data);

    if (commandStatus == ErrorCodes::LockBusy && !migration->oldShard) {
        migration->oldShard = true;

        stdx::lock_guard<stdx::mutex> lk(_mutex);
        _rescheduleMigration(*migration);
        return;
    }

    // This extra parsing below is in order to preserve backwards compatibility with 3.2 and
    // earlier, where the move chunk command instead of returning a ChunkTooBig status includes an
    // extra field in the response.
    if (!commandStatus.isOK()) {
        bool chunkTooBig = false;
        bsonExtractBooleanFieldWithDefault(
            remoteCommandResponse.data, kChunkTooBig, false, &chunkTooBig);
        if (chunkTooBig) {
            commandStatus = {ErrorCodes::ChunkTooBig, commandStatus.reason()};
        }
    }

    stdx::lock_guard<stdx::mutex> lk(_mutex);
    migrationStatuses->insert(MigrationStatuses::value_type(
        migration->chunkInfo.migrateInfo.getName(), std::move(commandStatus)));
}
    Status parseAuthSchemaUpgradeStepCommand(const BSONObj& cmdObj,
                                             const std::string& dbname,
                                             int* maxSteps,
                                             bool* shouldUpgradeShards,
                                             BSONObj* parsedWriteConcern) {
        static const int minUpgradeSteps = 1;
        static const int maxUpgradeSteps = 2;

        unordered_set<std::string> validFieldNames;
        validFieldNames.insert("authSchemaUpgrade");
        validFieldNames.insert("maxSteps");
        validFieldNames.insert("upgradeShards");
        validFieldNames.insert("writeConcern");

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

        status = bsonExtractBooleanFieldWithDefault(
                cmdObj, "upgradeShards", true, shouldUpgradeShards);
        if (!status.isOK()) {
            return status;
        }

        long long steps;
        status = bsonExtractIntegerFieldWithDefault(cmdObj, "maxSteps", maxUpgradeSteps, &steps);
        if (!status.isOK())
            return status;
        if (steps < minUpgradeSteps || steps > maxUpgradeSteps) {
            return Status(ErrorCodes::BadValue, mongoutils::str::stream() <<
                          "Legal values for \"maxSteps\" are at least " << minUpgradeSteps <<
                          " and no more than " << maxUpgradeSteps << "; found " << steps);
        }
        *maxSteps = static_cast<int>(steps);

        status = _extractWriteConcern(cmdObj, parsedWriteConcern);
        if (!status.isOK()) {
            return status;
        }

        return Status::OK();
    }
Exemple #9
0
    Status doSaslStart(SaslAuthenticationSession* session,
                       const std::string& db, 
                       const BSONObj& cmdObj,
                       BSONObjBuilder* result) {

        bool autoAuthorize = false;
        Status status = bsonExtractBooleanFieldWithDefault(cmdObj,
                                                           saslCommandAutoAuthorizeFieldName,
                                                           autoAuthorizeDefault,
                                                           &autoAuthorize);
        if (!status.isOK())
            return status;

        std::string mechanism;
        status = extractMechanism(cmdObj, &mechanism);
        if (!status.isOK())
            return status;

        
        if (!sequenceContains(saslGlobalParams.authenticationMechanisms, mechanism)) {
            result->append(saslCommandMechanismListFieldName,
                           saslGlobalParams.authenticationMechanisms);
            return Status(ErrorCodes::BadValue,
                          mongoutils::str::stream() << "Unsupported mechanism " << mechanism);
        }

        status = session->start(db,
                                mechanism,
                                saslGlobalParams.serviceName,
                                saslGlobalParams.hostName,
                                1,
                                autoAuthorize);
        if (!status.isOK())
            return status;

        return doSaslStep(session, cmdObj, result);
    }
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 configServerConnectionString;
        Status status = bsonExtractStringField(
            obj, kConfigServerConnectionString, &configServerConnectionString);
        if (!status.isOK()) {
            return status;
        }

        auto statusConfigServerCS = ConnectionString::parse(configServerConnectionString);
        if (!statusConfigServerCS.isOK()) {
            return statusConfigServerCS.getStatus();
        }

        request._configServerCS = std::move(statusConfigServerCS.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;
        }
    }

    {
        Status status =
            bsonExtractBooleanFieldWithDefault(obj, kWaitForDelete, 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);
    }

    {
        Status status =
            bsonExtractBooleanFieldWithDefault(obj, kTakeDistLock, true, &request._takeDistLock);
        if (!status.isOK()) {
            return status;
        }
    }

    return request;
}
StatusWith<SetShardVersionRequest> SetShardVersionRequest::parseFromBSON(const BSONObj& cmdObj) {
    SetShardVersionRequest request;

    {
        std::string configServer;
        Status status = bsonExtractStringField(cmdObj, kConfigServer, &configServer);
        if (!status.isOK())
            return status;

        auto configServerStatus = ConnectionString::parse(configServer);
        if (!configServerStatus.isOK())
            return configServerStatus.getStatus();

        request._configServer = std::move(configServerStatus.getValue());
    }

    {
        Status status = bsonExtractStringField(cmdObj, kShardName, &request._shardName);
        if (!status.isOK())
            return status;
    }

    {
        std::string shardCS;
        Status status = bsonExtractStringField(cmdObj, kShardConnectionString, &shardCS);
        if (!status.isOK())
            return status;

        auto shardCSStatus = ConnectionString::parse(shardCS);
        if (!shardCSStatus.isOK())
            return shardCSStatus.getStatus();

        request._shardCS = std::move(shardCSStatus.getValue());
    }

    {
        Status status = bsonExtractBooleanFieldWithDefault(cmdObj, kInit, false, &request._init);
        if (!status.isOK())
            return status;
    }

    {
        Status status = bsonExtractBooleanFieldWithDefault(
            cmdObj, kAuthoritative, false, &request._isAuthoritative);
        if (!status.isOK())
            return status;
    }

    {
        Status status = bsonExtractBooleanFieldWithDefault(
            cmdObj, kNoConnectionVersioning, false, &request._noConnectionVersioning);
        if (!status.isOK())
            return status;
    }

    if (request.isInit()) {
        return request;
    }

    // Only initialize the version information if this is not an "init" request

    {
        std::string ns;
        Status status = bsonExtractStringField(cmdObj, kCmdName, &ns);
        if (!status.isOK())
            return status;

        NamespaceString nss(ns);

        if (!nss.isValid()) {
            return {ErrorCodes::InvalidNamespace,
                    str::stream() << ns << " is not a valid namespace"};
        }

        request._nss = std::move(nss);
    }

    {
        auto versionStatus = ChunkVersionAndOpTime::parseFromBSONForSetShardVersion(cmdObj);
        if (!versionStatus.isOK())
            return versionStatus.getStatus();

        request._version = versionStatus.getValue();
    }

    return request;
}
StatusWith<BalancerSettingsType> BalancerSettingsType::fromBSON(const BSONObj& obj) {
    BalancerSettingsType settings;

    {
        bool stopped;
        Status status = bsonExtractBooleanFieldWithDefault(obj, kStopped, false, &stopped);
        if (!status.isOK())
            return status;
        if (stopped) {
            settings._mode = kOff;
        } else {
            std::string modeStr;
            status = bsonExtractStringFieldWithDefault(obj, kMode, kBalancerModes[kFull], &modeStr);
            if (!status.isOK())
                return status;
            auto it = std::find(std::begin(kBalancerModes), std::end(kBalancerModes), modeStr);
            if (it == std::end(kBalancerModes)) {
                return Status(ErrorCodes::BadValue, "Invalid balancer mode");
            }

            settings._mode = static_cast<BalancerMode>(it - std::begin(kBalancerModes));
        }
    }

    {
        BSONElement activeWindowElem;
        Status status = bsonExtractTypedField(obj, kActiveWindow, Object, &activeWindowElem);
        if (status.isOK()) {
            const BSONObj balancingWindowObj = activeWindowElem.Obj();
            if (balancingWindowObj.isEmpty()) {
                return Status(ErrorCodes::BadValue, "activeWindow not specified");
            }

            // Check if both 'start' and 'stop' are present
            const std::string start = balancingWindowObj.getField("start").str();
            const std::string stop = balancingWindowObj.getField("stop").str();

            if (start.empty() || stop.empty()) {
                return Status(ErrorCodes::BadValue,
                              str::stream()
                                  << "must specify both start and stop of balancing window: "
                                  << balancingWindowObj);
            }

            // Check that both 'start' and 'stop' are valid time-of-day
            boost::posix_time::ptime startTime;
            boost::posix_time::ptime stopTime;
            if (!toPointInTime(start, &startTime) || !toPointInTime(stop, &stopTime)) {
                return Status(ErrorCodes::BadValue,
                              str::stream() << kActiveWindow << " format is "
                                            << " { start: \"hh:mm\" , stop: \"hh:mm\" }");
            }

            // Check that start and stop designate different time points
            if (startTime == stopTime) {
                return Status(ErrorCodes::BadValue,
                              str::stream() << "start and stop times must be different");
            }

            settings._activeWindowStart = startTime;
            settings._activeWindowStop = stopTime;
        } else if (status != ErrorCodes::NoSuchKey) {
            return status;
        }
    }

    {
        auto secondaryThrottleStatus =
            MigrationSecondaryThrottleOptions::createFromBalancerConfig(obj);
        if (!secondaryThrottleStatus.isOK()) {
            return secondaryThrottleStatus.getStatus();
        }

        settings._secondaryThrottle = std::move(secondaryThrottleStatus.getValue());
    }

    {
        bool waitForDelete;
        Status status =
            bsonExtractBooleanFieldWithDefault(obj, kWaitForDelete, false, &waitForDelete);
        if (!status.isOK())
            return status;

        settings._waitForDelete = waitForDelete;
    }

    return settings;
}
Exemple #13
0
Status applyOps(OperationContext* opCtx,
                const std::string& dbName,
                const BSONObj& applyOpCmd,
                BSONObjBuilder* result) {
    bool allowAtomic = false;
    uassertStatusOK(
        bsonExtractBooleanFieldWithDefault(applyOpCmd, "allowAtomic", true, &allowAtomic));
    auto areOpsCrudOnly = _areOpsCrudOnly(applyOpCmd);
    auto isAtomic = allowAtomic && areOpsCrudOnly;
    auto hasPrecondition = _hasPrecondition(applyOpCmd);

    boost::optional<Lock::GlobalWrite> globalWriteLock;
    boost::optional<Lock::DBLock> dbWriteLock;

    // There's only one case where we are allowed to take the database lock instead of the global
    // lock - no preconditions; only CRUD ops; and non-atomic mode.
    if (!hasPrecondition && areOpsCrudOnly && !allowAtomic) {
        dbWriteLock.emplace(opCtx, dbName, MODE_X);
    } else {
        globalWriteLock.emplace(opCtx);
    }

    bool userInitiatedWritesAndNotPrimary = opCtx->writesAreReplicated() &&
        !repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(opCtx, dbName);

    if (userInitiatedWritesAndNotPrimary)
        return Status(ErrorCodes::NotMaster,
                      str::stream() << "Not primary while applying ops to database " << dbName);

    if (hasPrecondition) {
        auto status = _checkPrecondition(opCtx, applyOpCmd, result);
        if (!status.isOK()) {
            return status;
        }
    }

    int numApplied = 0;
    if (!isAtomic)
        return _applyOps(opCtx, dbName, applyOpCmd, result, &numApplied);

    // Perform write ops atomically
    invariant(globalWriteLock);
    try {
        writeConflictRetry(opCtx, "applyOps", dbName, [&] {
            BSONObjBuilder intermediateResult;
            WriteUnitOfWork wunit(opCtx);
            numApplied = 0;
            {
                // Suppress replication for atomic operations until end of applyOps.
                repl::UnreplicatedWritesBlock uwb(opCtx);
                uassertStatusOK(
                    _applyOps(opCtx, dbName, applyOpCmd, &intermediateResult, &numApplied));
            }
            // Generate oplog entry for all atomic ops collectively.
            if (opCtx->writesAreReplicated()) {
                // We want this applied atomically on slaves so we rewrite the oplog entry without
                // the pre-condition for speed.

                BSONObjBuilder cmdBuilder;

                for (auto elem : applyOpCmd) {
                    auto name = elem.fieldNameStringData();
                    if (name == kPreconditionFieldName)
                        continue;
                    if (name == "bypassDocumentValidation")
                        continue;
                    cmdBuilder.append(elem);
                }

                const BSONObj cmdRewritten = cmdBuilder.done();

                auto opObserver = getGlobalServiceContext()->getOpObserver();
                invariant(opObserver);
                opObserver->onApplyOps(opCtx, dbName, cmdRewritten);
            }
            wunit.commit();
            result->appendElements(intermediateResult.obj());
        });
    } catch (const DBException& ex) {
        if (ex.getCode() == ErrorCodes::NamespaceNotFound) {
            // Retry in non-atomic mode, since MMAP cannot implicitly create a new database
            // within an active WriteUnitOfWork.
            return _applyOps(opCtx, dbName, applyOpCmd, result, &numApplied);
        }
        BSONArrayBuilder ab;
        ++numApplied;
        for (int j = 0; j < numApplied; j++)
            ab.append(false);
        result->append("applied", numApplied);
        result->append("code", ex.getCode());
        result->append("codeName", ErrorCodes::errorString(ErrorCodes::fromInt(ex.getCode())));
        result->append("errmsg", ex.what());
        result->append("results", ab.arr());
        return Status(ErrorCodes::UnknownError, ex.what());
    }

    return Status::OK();
}
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;
}
Exemple #15
0
StatusWith<MoveChunkRequest> MoveChunkRequest::createFromCommand(NamespaceString nss,
                                                                 const BSONObj& obj) {
    auto secondaryThrottleStatus = MigrationSecondaryThrottleOptions::createFromCommand(obj);
    if (!secondaryThrottleStatus.isOK()) {
        return secondaryThrottleStatus.getStatus();
    }

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

    {
        std::string configServerConnectionString;
        Status status = bsonExtractStringField(
            obj, kConfigServerConnectionString, &configServerConnectionString);
        if (!status.isOK()) {
            return status;
        }

        auto statusConfigServerCS = ConnectionString::parse(configServerConnectionString);
        if (!statusConfigServerCS.isOK()) {
            return statusConfigServerCS.getStatus();
        }

        request._configServerCS = std::move(statusConfigServerCS.getValue());
    }

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

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

    {
        BSONElement elem;
        Status status = bsonExtractTypedField(obj, kChunkMinKey, BSONType::Object, &elem);
        if (!status.isOK()) {
            return status;
        }

        request._minKey = elem.Obj().getOwned();

        if (request._minKey.isEmpty()) {
            return Status(ErrorCodes::UnsupportedFormat, "The chunk min key cannot be empty");
        }
    }

    {
        BSONElement elem;
        Status status = bsonExtractTypedField(obj, kChunkMaxKey, BSONType::Object, &elem);
        if (!status.isOK()) {
            return status;
        }

        request._maxKey = elem.Obj().getOwned();

        if (request._maxKey.isEmpty()) {
            return Status(ErrorCodes::UnsupportedFormat, "The chunk max key cannot be empty");
        }
    }

    {
        Status status =
            bsonExtractBooleanFieldWithDefault(obj, kWaitForDelete, 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);
    }

    return request;
}
Exemple #16
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;
}
Exemple #17
0
Status ReplicaSetConfig::_parseSettingsSubdocument(const BSONObj& settings) {
    //
    // Parse heartbeatIntervalMillis
    //
    long long heartbeatIntervalMillis;
    Status hbIntervalStatus =
        bsonExtractIntegerFieldWithDefault(settings,
                                           kHeartbeatIntervalFieldName,
                                           durationCount<Milliseconds>(kDefaultHeartbeatInterval),
                                           &heartbeatIntervalMillis);
    if (!hbIntervalStatus.isOK()) {
        return hbIntervalStatus;
    }
    _heartbeatInterval = Milliseconds(heartbeatIntervalMillis);

    // Parse electionTimeoutMillis
    //
    BSONElement electionTimeoutMillisElement = settings[kElectionTimeoutFieldName];
    if (electionTimeoutMillisElement.eoo()) {
        _electionTimeoutPeriod = Milliseconds(kDefaultElectionTimeoutPeriod);
    } else if (electionTimeoutMillisElement.isNumber()) {
        _electionTimeoutPeriod = Milliseconds(electionTimeoutMillisElement.numberInt());
    } else {
        return Status(ErrorCodes::TypeMismatch,
                      str::stream() << "Expected type of " << kSettingsFieldName << "."
                                    << kElectionTimeoutFieldName
                                    << " to be a number, but found a value of type "
                                    << typeName(electionTimeoutMillisElement.type()));
    }

    //
    // Parse heartbeatTimeoutSecs
    //
    BSONElement hbTimeoutSecsElement = settings[kHeartbeatTimeoutFieldName];
    if (hbTimeoutSecsElement.eoo()) {
        _heartbeatTimeoutPeriod = Seconds(kDefaultHeartbeatTimeoutPeriod);
    } else if (hbTimeoutSecsElement.isNumber()) {
        _heartbeatTimeoutPeriod = Seconds(hbTimeoutSecsElement.numberInt());
    } else {
        return Status(ErrorCodes::TypeMismatch,
                      str::stream() << "Expected type of " << kSettingsFieldName << "."
                                    << kHeartbeatTimeoutFieldName
                                    << " to be a number, but found a value of type "
                                    << typeName(hbTimeoutSecsElement.type()));
    }

    //
    // Parse chainingAllowed
    //
    Status status = bsonExtractBooleanFieldWithDefault(
        settings, kChainingAllowedFieldName, true, &_chainingAllowed);
    if (!status.isOK())
        return status;

    //
    // Parse getLastErrorDefaults
    //
    BSONElement gleDefaultsElement;
    status = bsonExtractTypedField(
        settings, kGetLastErrorDefaultsFieldName, Object, &gleDefaultsElement);
    if (status.isOK()) {
        status = _defaultWriteConcern.parse(gleDefaultsElement.Obj());
        if (!status.isOK())
            return status;
    } else if (status == ErrorCodes::NoSuchKey) {
        // Default write concern is w: 1.
        _defaultWriteConcern.reset();
        _defaultWriteConcern.wNumNodes = 1;
    } else {
        return status;
    }

    //
    // Parse getLastErrorModes
    //
    BSONElement gleModesElement;
    status = bsonExtractTypedField(settings, kGetLastErrorModesFieldName, Object, &gleModesElement);
    BSONObj gleModes;
    if (status.isOK()) {
        gleModes = gleModesElement.Obj();
    } else if (status != ErrorCodes::NoSuchKey) {
        return status;
    }

    for (BSONObj::iterator gleModeIter(gleModes); gleModeIter.more();) {
        const BSONElement modeElement = gleModeIter.next();
        if (_customWriteConcernModes.find(modeElement.fieldNameStringData()) !=
            _customWriteConcernModes.end()) {
            return Status(ErrorCodes::DuplicateKey,
                          str::stream() << kSettingsFieldName << '.' << kGetLastErrorModesFieldName
                                        << " contains multiple fields named "
                                        << modeElement.fieldName());
        }
        if (modeElement.type() != Object) {
            return Status(ErrorCodes::TypeMismatch,
                          str::stream() << "Expected " << kSettingsFieldName << '.'
                                        << kGetLastErrorModesFieldName << '.'
                                        << modeElement.fieldName() << " to be an Object, not "
                                        << typeName(modeElement.type()));
        }
        ReplicaSetTagPattern pattern = _tagConfig.makePattern();
        for (BSONObj::iterator constraintIter(modeElement.Obj()); constraintIter.more();) {
            const BSONElement constraintElement = constraintIter.next();
            if (!constraintElement.isNumber()) {
                return Status(ErrorCodes::TypeMismatch,
                              str::stream()
                                  << "Expected " << kSettingsFieldName << '.'
                                  << kGetLastErrorModesFieldName << '.' << modeElement.fieldName()
                                  << '.' << constraintElement.fieldName() << " to be a number, not "
                                  << typeName(constraintElement.type()));
            }
            const int minCount = constraintElement.numberInt();
            if (minCount <= 0) {
                return Status(ErrorCodes::BadValue,
                              str::stream() << "Value of " << kSettingsFieldName << '.'
                                            << kGetLastErrorModesFieldName << '.'
                                            << modeElement.fieldName() << '.'
                                            << constraintElement.fieldName()
                                            << " must be positive, but found " << minCount);
            }
            status = _tagConfig.addTagCountConstraintToPattern(
                &pattern, constraintElement.fieldNameStringData(), minCount);
            if (!status.isOK()) {
                return status;
            }
        }
        _customWriteConcernModes[modeElement.fieldNameStringData()] = pattern;
    }

    return Status::OK();
}
        // TODO: The bulk of the implementation of this will need to change once we're using the
        // new v2 authorization storage format.
        bool run(const string& dbname,
                 BSONObj& cmdObj,
                 int options,
                 string& errmsg,
                 BSONObjBuilder& result,
                 bool fromRepl) {
            std::string userName;
            std::string password;
            std::string userSource; // TODO: remove this.
            bool readOnly; // TODO: remove this.
            BSONElement extraData;
            BSONElement roles;

            if (cmdObj.hasField("pwd") && cmdObj.hasField("userSource")) {
                errmsg = "User objects can't have both 'pwd' and 'userSource'";
                return false;
            }

            if (!cmdObj.hasField("pwd") && !cmdObj.hasField("userSource")) {
                errmsg = "User objects must have one of 'pwd' and 'userSource'";
                return false;
            }

            if (cmdObj.hasField("roles") && cmdObj.hasField("readOnly")) {
                errmsg = "User objects can't have both 'roles' and 'readOnly'";
                return false;
            }

            Status status = bsonExtractStringField(cmdObj, "user", &userName);
            if (!status.isOK()) {
                addStatus(Status(ErrorCodes::UserModificationFailed,
                                 "\"user\" string not specified"),
                          result);
                return false;
            }

            status = bsonExtractStringFieldWithDefault(cmdObj, "pwd", "", &password);
            if (!status.isOK()) {
                addStatus(Status(ErrorCodes::UserModificationFailed,
                                 "Invalid \"pwd\" string"),
                          result);
                return false;
            }

            status = bsonExtractStringFieldWithDefault(cmdObj, "userSource", "", &userSource);
            if (!status.isOK()) {
                addStatus(Status(ErrorCodes::UserModificationFailed,
                                 "Invalid \"userSource\" string"),
                          result);
                return false;
            }

            status = bsonExtractBooleanFieldWithDefault(cmdObj, "readOnly", false, &readOnly);
            if (!status.isOK()) {
                addStatus(Status(ErrorCodes::UserModificationFailed,
                                 "Invalid \"readOnly\" boolean"),
                          result);
                return false;
            }

            if (cmdObj.hasField("extraData")) {
                status = bsonExtractField(cmdObj, "extraData", &extraData);
                if (!status.isOK()) {
                    addStatus(Status(ErrorCodes::UserModificationFailed,
                                     "Invalid \"extraData\" object"),
                              result);
                    return false;
                }
            }

            if (cmdObj.hasField("roles")) {
                status = bsonExtractField(cmdObj, "roles", &roles);
                if (!status.isOK()) {
                    addStatus(Status(ErrorCodes::UserModificationFailed,
                                     "Invalid \"roles\" array"),
                              result);
                    return false;
                }
            }

            BSONObjBuilder userObjBuilder;
            userObjBuilder.append("user", userName);
            if (cmdObj.hasField("pwd")) {
                // TODO: hash password once we're receiving plaintext passwords here.
                userObjBuilder.append("pwd", password);
            }

            if (cmdObj.hasField("userSource")) {
                userObjBuilder.append("userSource", userSource);
            }

            if (cmdObj.hasField("readOnly")) {
                userObjBuilder.append("readOnly", readOnly);
            }

            if (cmdObj.hasField("extraData")) {
                userObjBuilder.append("extraData", extraData);
            }

            if (cmdObj.hasField("roles")) {
                userObjBuilder.append(roles);
            }

            status = getGlobalAuthorizationManager()->insertPrivilegeDocument(dbname,
                                                                              userObjBuilder.obj());
            if (!status.isOK()) {
                addStatus(status, result);
                return false;
            }

            return true;
        }
Exemple #19
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 = bsonExtractBooleanFieldWithDefault(source,
                                                               jumbo.name(),
                                                               false,
                                                               &chunkJumbo);
            if (!status.isOK()) return status;
            chunk._jumbo = chunkJumbo;
        }

        //
        // 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;
    }
Exemple #20
0
Status ReplicaSetConfig::initialize(const BSONObj& cfg) {
    _isInitialized = false;
    _members.clear();
    Status status =
        bsonCheckOnlyHasFields("replica set configuration", cfg, kLegalConfigTopFieldNames);
    if (!status.isOK())
        return status;

    //
    // Parse replSetName
    //
    status = bsonExtractStringField(cfg, kIdFieldName, &_replSetName);
    if (!status.isOK())
        return status;

    //
    // Parse version
    //
    status = bsonExtractIntegerField(cfg, kVersionFieldName, &_version);
    if (!status.isOK())
        return status;

    //
    // Parse members
    //
    BSONElement membersElement;
    status = bsonExtractTypedField(cfg, kMembersFieldName, Array, &membersElement);
    if (!status.isOK())
        return status;

    for (BSONObj::iterator membersIterator(membersElement.Obj()); membersIterator.more();) {
        BSONElement memberElement = membersIterator.next();
        if (memberElement.type() != Object) {
            return Status(ErrorCodes::TypeMismatch,
                          str::stream() << "Expected type of " << kMembersFieldName << "."
                                        << memberElement.fieldName() << " to be Object, but found "
                                        << typeName(memberElement.type()));
        }
        _members.resize(_members.size() + 1);
        const auto& memberBSON = memberElement.Obj();
        status = _members.back().initialize(memberBSON, &_tagConfig);
        if (!status.isOK())
            return Status(ErrorCodes::InvalidReplicaSetConfig,
                          str::stream() << status.toString() << " for member:" << memberBSON);
    }

    //
    // Parse configServer
    //
    status = bsonExtractBooleanFieldWithDefault(cfg, kConfigServerFieldName, false, &_configServer);
    if (!status.isOK()) {
        return status;
    }

    //
    // Parse protocol version
    //
    status = bsonExtractIntegerField(cfg, kProtocolVersionFieldName, &_protocolVersion);
    if (!status.isOK() && status != ErrorCodes::NoSuchKey) {
        return status;
    }

    //
    // Parse settings
    //
    BSONElement settingsElement;
    status = bsonExtractTypedField(cfg, kSettingsFieldName, Object, &settingsElement);
    BSONObj settings;
    if (status.isOK()) {
        settings = settingsElement.Obj();
    } else if (status != ErrorCodes::NoSuchKey) {
        return status;
    }
    status = _parseSettingsSubdocument(settings);
    if (!status.isOK())
        return status;

    _calculateMajorities();
    _addInternalWriteConcernModes();
    _isInitialized = true;
    return Status::OK();
}
    Status parseCreateOrUpdateUserCommands(const BSONObj& cmdObj,
                                           const StringData& cmdName,
                                           const std::string& dbname,
                                           CreateOrUpdateUserArgs* parsedArgs) {
        unordered_set<std::string> validFieldNames;
        validFieldNames.insert(cmdName.toString());
        validFieldNames.insert("customData");
        validFieldNames.insert("digestPassword");
        validFieldNames.insert("pwd");
        validFieldNames.insert("roles");
        validFieldNames.insert("writeConcern");

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

        status = _extractWriteConcern(cmdObj, &parsedArgs->writeConcern);
        if (!status.isOK()) {
            return status;
        }

        BSONObjBuilder userObjBuilder;

        // Parse user name
        std::string userName;
        status = bsonExtractStringField(cmdObj, cmdName, &userName);
        if (!status.isOK()) {
            return status;
        }

        parsedArgs->userName = UserName(userName, dbname);

        // Parse password
        if (cmdObj.hasField("pwd")) {
            std::string password;
            status = bsonExtractStringField(cmdObj, "pwd", &password);
            if (!status.isOK()) {
                return status;
            }
            if (password.empty()) {
                return Status(ErrorCodes::BadValue, "User passwords must not be empty");
            }

            bool digestPassword; // True if the server should digest the password
            status = bsonExtractBooleanFieldWithDefault(cmdObj,
                                                        "digestPassword",
                                                        true,
                                                        &digestPassword);
            if (!status.isOK()) {
                return status;
            }

            if (digestPassword) {
                parsedArgs->hashedPassword = auth::createPasswordDigest(userName, password);
            } else {
                parsedArgs->hashedPassword = password;
            }
            parsedArgs->hasHashedPassword = true;
        }

        // Parse custom data
        if (cmdObj.hasField("customData")) {
            BSONElement element;
            status = bsonExtractTypedField(cmdObj, "customData", Object, &element);
            if (!status.isOK()) {
                return status;
            }
            parsedArgs->customData = element.Obj();
            parsedArgs->hasCustomData = true;
        }

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

        return Status::OK();
    }
Exemple #22
0
    Status MemberConfig::initialize(const BSONObj& mcfg, ReplicaSetTagConfig* tagConfig) {
        Status status = bsonCheckOnlyHasFields(
            "replica set member configuration", mcfg, kLegalMemberConfigFieldNames);
        if (!status.isOK())
            return status;

        //
        // Parse _id field.
        //
        BSONElement idElement = mcfg[kIdFieldName];
        if (idElement.eoo()) {
            return Status(ErrorCodes::NoSuchKey, str::stream() << kIdFieldName <<
                          " field is missing");
        }
        if (!idElement.isNumber()) {
            return Status(ErrorCodes::TypeMismatch, str::stream() << kIdFieldName <<
                          " field has non-numeric type " << typeName(idElement.type()));
        }
        _id = idElement.numberInt();

        //
        // Parse h field.
        //
        std::string hostAndPortString;
        status = bsonExtractStringField(mcfg, kHostFieldName, &hostAndPortString);
        if (!status.isOK())
            return status;
        boost::trim(hostAndPortString);
        status = _host.initialize(hostAndPortString);
        if (!status.isOK())
            return status;
        if (!_host.hasPort()) {
            // make port explicit even if default.
            _host = HostAndPort(_host.host(), _host.port());
        }

        //
        // Parse votes field.
        //
        BSONElement votesElement = mcfg[kVotesFieldName];
        int votes;
        if (votesElement.eoo()) {
            votes = kVotesFieldDefault;
        }
        else if (votesElement.isNumber()) {
            votes = votesElement.numberInt();
        }
        else {
            return Status(ErrorCodes::TypeMismatch, str::stream() << kVotesFieldName <<
                          " field value has non-numeric type " <<
                          typeName(votesElement.type()));
        }
        if (votes != 0 && votes != 1) {
            return Status(ErrorCodes::BadValue, str::stream() << kVotesFieldName <<
                          " field value is " << votesElement.numberInt() << " but must be 0 or 1");
        }
        _isVoter = bool(votes);

        //
        // Parse priority field.
        //
        BSONElement priorityElement = mcfg[kPriorityFieldName];
        if (priorityElement.eoo()) {
            _priority = kPriorityFieldDefault;
        }
        else if (priorityElement.isNumber()) {
            _priority = priorityElement.numberDouble();
        }
        else {
            return Status(ErrorCodes::TypeMismatch, str::stream() << kPriorityFieldName <<
                          " field has non-numeric type " << typeName(priorityElement.type()));
        }

        //
        // Parse arbiterOnly field.
        //
        status = bsonExtractBooleanFieldWithDefault(mcfg,
                                                    kArbiterOnlyFieldName,
                                                    kArbiterOnlyFieldDefault,
                                                    &_arbiterOnly);
        if (!status.isOK())
            return status;

        //
        // Parse slaveDelay field.
        //
        BSONElement slaveDelayElement = mcfg[kSlaveDelayFieldName];
        if (slaveDelayElement.eoo()) {
            _slaveDelay = kSlaveDelayFieldDefault;
        }
        else if (slaveDelayElement.isNumber()) {
            _slaveDelay = Seconds(slaveDelayElement.numberInt());
        }
        else {
            return Status(ErrorCodes::TypeMismatch, str::stream() << kSlaveDelayFieldName <<
                          " field value has non-numeric type " <<
                          typeName(slaveDelayElement.type()));
        }

        //
        // Parse hidden field.
        //
        status = bsonExtractBooleanFieldWithDefault(mcfg,
                                                    kHiddenFieldName,
                                                    kHiddenFieldDefault,
                                                    &_hidden);
        if (!status.isOK())
            return status;

        //
        // Parse buildIndexes field.
        //
        status = bsonExtractBooleanFieldWithDefault(mcfg,
                                                    kBuildIndexesFieldName,
                                                    kBuildIndexesFieldDefault,
                                                    &_buildIndexes);
        if (!status.isOK())
            return status;

        //
        // Parse "tags" field.
        //
        _tags.clear();
        BSONElement tagsElement;
        status = bsonExtractTypedField(mcfg, kTagsFieldName, Object, &tagsElement);
        if (status.isOK()) {
            for (BSONObj::iterator tagIter(tagsElement.Obj()); tagIter.more();) {
                const BSONElement& tag = tagIter.next();
                if (tag.type() != String) {
                    return Status(ErrorCodes::TypeMismatch, str::stream() << "tags." <<
                                  tag.fieldName() << " field has non-string value of type " <<
                                  typeName(tag.type()));
                }
                _tags.push_back(tagConfig->makeTag(tag.fieldNameStringData(),
                                                   tag.valueStringData()));
            }
        }
        else if (ErrorCodes::NoSuchKey != status) {
            return status;
        }

        return Status::OK();
    }
StatusWith<BalanceChunkRequest> BalanceChunkRequest::parseFromConfigCommand(const BSONObj& obj) {
    auto chunkStatus = ChunkType::fromBSON(obj);
    if (!chunkStatus.isOK()) {
        return chunkStatus.getStatus();
    }

    // The secondary throttle options being sent to the config server are contained within a
    // sub-object on the request because they contain the writeConcern field, which when sent to the
    // config server gets checked for only being w:1 or w:majoirty.
    BSONObj secondaryThrottleObj;

    {
        BSONElement secondaryThrottleElement;
        auto secondaryThrottleElementStatus =
            bsonExtractTypedField(obj, kSecondaryThrottle, Object, &secondaryThrottleElement);

        if (secondaryThrottleElementStatus.isOK()) {
            secondaryThrottleObj = secondaryThrottleElement.Obj();
        } else if (secondaryThrottleElementStatus != ErrorCodes::NoSuchKey) {
            return secondaryThrottleElementStatus;
        }
    }

    auto secondaryThrottleStatus =
        MigrationSecondaryThrottleOptions::createFromCommand(secondaryThrottleObj);
    if (!secondaryThrottleStatus.isOK()) {
        return secondaryThrottleStatus.getStatus();
    }

    BalanceChunkRequest request(std::move(chunkStatus.getValue()),
                                std::move(secondaryThrottleStatus.getValue()));

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

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

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

    {
        std::string toShardId;
        Status status = bsonExtractStringField(obj, kToShardId, &toShardId);
        if (status.isOK()) {
            if (toShardId.empty()) {
                return {ErrorCodes::BadValue, "To shard cannot be empty"};
            }

            request._toShardId = std::move(toShardId);
        } else if (status != ErrorCodes::NoSuchKey) {
            return status;
        }
    }

    return request;
}