/* Copyright 2012 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #include "mongo/platform/basic.h" #include "mongo/dbtests/mock/mock_remote_db_server.h" #include #include "mongo/dbtests/mock/mock_dbclient_connection.h" #include "mongo/rpc/command_reply.h" #include "mongo/rpc/command_reply_builder.h" #include "mongo/rpc/metadata.h" #include "mongo/stdx/memory.h" #include "mongo/util/assert_util.h" #include "mongo/util/mongoutils/str.h" #include "mongo/util/net/socket_exception.h" #include "mongo/util/time_support.h" using std::string; using std::vector; namespace mongo { MockRemoteDBServer::CircularBSONIterator::CircularBSONIterator(const vector& replyVector) { for (std::vector::const_iterator iter = replyVector.begin(); iter != replyVector.end(); ++iter) { _replyObjs.push_back(iter->copy()); } _iter = _replyObjs.begin(); } BSONObj MockRemoteDBServer::CircularBSONIterator::next() { verify(_iter != _replyObjs.end()); BSONObj reply = _iter->copy(); ++_iter; if (_iter == _replyObjs.end()) { _iter = _replyObjs.begin(); } return reply; } MockRemoteDBServer::MockRemoteDBServer(const string& hostAndPort) : _isRunning(true), _hostAndPort(hostAndPort), _delayMilliSec(0), _cmdCount(0), _queryCount(0), _instanceID(0) { insert(IdentityNS, BSON(HostField(hostAndPort)), 0); setCommandReply("dbStats", BSON(HostField(hostAndPort))); } MockRemoteDBServer::~MockRemoteDBServer() {} void MockRemoteDBServer::setDelay(long long milliSec) { scoped_spinlock sLock(_lock); _delayMilliSec = milliSec; } void MockRemoteDBServer::shutdown() { scoped_spinlock sLock(_lock); _isRunning = false; } void MockRemoteDBServer::reboot() { scoped_spinlock sLock(_lock); _isRunning = true; _instanceID++; } MockRemoteDBServer::InstanceID MockRemoteDBServer::getInstanceID() const { scoped_spinlock sLock(_lock); return _instanceID; } bool MockRemoteDBServer::isRunning() const { scoped_spinlock sLock(_lock); return _isRunning; } void MockRemoteDBServer::setCommandReply(const string& cmdName, const mongo::BSONObj& replyObj) { vector replySequence; replySequence.push_back(replyObj); setCommandReply(cmdName, replySequence); } void MockRemoteDBServer::setCommandReply(const string& cmdName, const vector& replySequence) { scoped_spinlock sLock(_lock); _cmdMap[cmdName].reset(new CircularBSONIterator(replySequence)); } void MockRemoteDBServer::insert(const string& ns, BSONObj obj, int flags) { scoped_spinlock sLock(_lock); vector& mockCollection = _dataMgr[ns]; mockCollection.push_back(obj.copy()); } void MockRemoteDBServer::remove(const string& ns, Query query, int flags) { scoped_spinlock sLock(_lock); if (_dataMgr.count(ns) == 0) { return; } _dataMgr.erase(ns); } rpc::UniqueReply MockRemoteDBServer::runCommandWithMetadata(MockRemoteDBServer::InstanceID id, StringData database, StringData commandName, const BSONObj& metadata, const BSONObj& commandArgs) { checkIfUp(id); std::string cmdName = commandName.toString(); BSONObj reply; { scoped_spinlock lk(_lock); uassert(ErrorCodes::IllegalOperation, str::stream() << "no reply for command: " << commandName, _cmdMap.count(cmdName)); reply = _cmdMap[cmdName]->next(); } if (_delayMilliSec > 0) { mongo::sleepmillis(_delayMilliSec); } checkIfUp(id); { scoped_spinlock lk(_lock); _cmdCount++; } // We need to construct a reply message - it will always be read through a view so it // doesn't matter whether we use CommandReplBuilder or LegacyReplyBuilder auto message = rpc::CommandReplyBuilder{} .setCommandReply(reply) .setMetadata(rpc::makeEmptyMetadata()) .done(); auto replyView = stdx::make_unique(&message); return rpc::UniqueReply(std::move(message), std::move(replyView)); } bool MockRemoteDBServer::runCommand(MockRemoteDBServer::InstanceID id, const string& dbname, const BSONObj& cmdObj, BSONObj& info, int options) { BSONObj upconvertedRequest; BSONObj upconvertedMetadata; std::tie(upconvertedRequest, upconvertedMetadata) = uassertStatusOK(rpc::upconvertRequestMetadata(cmdObj, options)); StringData commandName = upconvertedRequest.firstElementFieldName(); auto res = runCommandWithMetadata(id, dbname, commandName, upconvertedMetadata, upconvertedRequest); info = res->getCommandReply().getOwned(); return info["ok"].trueValue(); } mongo::BSONArray MockRemoteDBServer::query(MockRemoteDBServer::InstanceID id, const string& ns, mongo::Query query, int nToReturn, int nToSkip, const BSONObj* fieldsToReturn, int queryOptions, int batchSize) { checkIfUp(id); if (_delayMilliSec > 0) { mongo::sleepmillis(_delayMilliSec); } checkIfUp(id); scoped_spinlock sLock(_lock); _queryCount++; const vector& coll = _dataMgr[ns]; BSONArrayBuilder result; for (vector::const_iterator iter = coll.begin(); iter != coll.end(); ++iter) { result.append(iter->copy()); } return BSONArray(result.obj()); } mongo::ConnectionString::ConnectionType MockRemoteDBServer::type() const { return mongo::ConnectionString::CUSTOM; } size_t MockRemoteDBServer::getCmdCount() const { scoped_spinlock sLock(_lock); return _cmdCount; } size_t MockRemoteDBServer::getQueryCount() const { scoped_spinlock sLock(_lock); return _queryCount; } void MockRemoteDBServer::clearCounters() { scoped_spinlock sLock(_lock); _cmdCount = 0; _queryCount = 0; } string MockRemoteDBServer::getServerAddress() const { return _hostAndPort; } string MockRemoteDBServer::toString() { return _hostAndPort; } void MockRemoteDBServer::checkIfUp(InstanceID id) const { scoped_spinlock sLock(_lock); if (!_isRunning || id < _instanceID) { throw mongo::SocketException(mongo::SocketException::CLOSED, _hostAndPort); } } }