/* 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. */ #pragma once #include #include #include "mongo/client/dbclientinterface.h" #include "mongo/db/jsobj.h" #include "mongo/platform/unordered_map.h" #include "mongo/rpc/unique_message.h" #include "mongo/util/concurrency/spin_lock.h" namespace mongo { const std::string IdentityNS("local.me"); const BSONField HostField("host"); /** * A very simple mock that acts like a database server. Every object keeps track of its own * InstanceID, which initially starts at zero and increments every time it is restarted. * This is primarily used for simulating the state of which old connections won't be able * to talk to the sockets that has already been closed on this server. * * Note: All operations on this server are protected by a lock. */ class MockRemoteDBServer { public: typedef size_t InstanceID; /** * Creates a new mock server. This can also be setup to work with the * ConnectionString class by using mongo::MockConnRegistry as follows: * * ConnectionString::setConnectionHook(MockConnRegistry::get()->getConnStrHook()); * MockRemoteDBServer server("$a:27017"); * MockConnRegistry::get()->addServer(&server); * * This allows clients using the ConnectionString::connect interface to create * connections to this server. The requirements to make this hook fully functional are: * * 1. hostAndPort of this server should start with $. * 2. No other instance has the same hostAndPort as this. * * This server will also contain the hostAndPort inside the IdentityNS * collection. This is convenient for testing query routing. * * @param hostAndPort the host name with port for this server. * * @see MockConnRegistry */ MockRemoteDBServer(const std::string& hostAndPort); virtual ~MockRemoteDBServer(); // // Connectivity methods // /** * Set a delay for calls to query and runCommand */ void setDelay(long long milliSec); /** * Shuts down this server. Any operations on this server with an InstanceID * less than or equal to the current one will throw a mongo::SocketException. * To bring the server up again, use the reboot method. */ void shutdown(); /** * Increments the instanceID of this server. */ void reboot(); /** * @return true if this server is running */ bool isRunning() const; // // Mocking methods // /** * Sets the reply for a command. * * @param cmdName the name of the command * @param replyObj the exact reply for the command */ void setCommandReply(const std::string& cmdName, const mongo::BSONObj& replyObj); /** * Sets the reply for a command. * * @param cmdName the name of the command. * @param replySequence the sequence of replies to cycle through every time * the given command is requested. This is useful for setting up a * sequence of response when the command can be called more than once * that requires different results when calling a method. */ void setCommandReply(const std::string& cmdName, const std::vector& replySequence); /** * Inserts a single document to this server. * * @param ns the namespace to insert the document to. * @param obj the document to insert. * @param flags ignored. */ void insert(const std::string& ns, BSONObj obj, int flags = 0); /** * Removes documents from this server. * * @param ns the namespace to remove documents from. * @param query ignored. * @param flags ignored. */ void remove(const std::string& ns, Query query, int flags = 0); // // DBClientBase methods // bool runCommand(InstanceID id, const std::string& dbname, const mongo::BSONObj& cmdObj, mongo::BSONObj &info, int options = 0); rpc::UniqueReply runCommandWithMetadata(InstanceID id, StringData database, StringData commandName, const BSONObj& metadata, const BSONObj& commandArgs); mongo::BSONArray query(InstanceID id, const std::string &ns, mongo::Query query = mongo::Query(), int nToReturn = 0, int nToSkip = 0, const mongo::BSONObj* fieldsToReturn = 0, int queryOptions = 0, int batchSize = 0); // // Getters // InstanceID getInstanceID() const; mongo::ConnectionString::ConnectionType type() const; double getSoTimeout() const; /** * @return the exact std::string address passed to hostAndPort parameter of the * constructor. In other words, doesn't automatically append a * 'default' port if none is specified. */ std::string getServerAddress() const; std::string toString(); // // Call counters // size_t getCmdCount() const; size_t getQueryCount() const; void clearCounters(); private: /** * A very simple class for cycling through a set of BSONObj */ class CircularBSONIterator { public: /** * Creates a new iterator with a deep copy of the vector. */ CircularBSONIterator(const std::vector& replyVector); mongo::BSONObj next(); private: std::vector::iterator _iter; std::vector _replyObjs; }; /** * Checks whether the instance of the server is still up. * * @throws mongo::SocketException if this server is down */ void checkIfUp(InstanceID id) const; typedef unordered_map > CmdToReplyObj; typedef unordered_map > MockDataMgr; bool _isRunning; const std::string _hostAndPort; long long _delayMilliSec; // // Mock replies // CmdToReplyObj _cmdMap; MockDataMgr _dataMgr; // // Op Counters // size_t _cmdCount; size_t _queryCount; // Unique id for every restart of this server used for rejecting requests from // connections that are still "connected" to the old instance InstanceID _instanceID; // protects this entire instance mutable mongo::SpinLock _lock; }; }