summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorA. Jesse Jiryu Davis <jesse@mongodb.com>2020-08-20 13:00:50 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-08-20 18:12:14 +0000
commitd3064df927c97abd402ee5768ab57667ed6849c9 (patch)
treec246336371b545bdc5942dab73aef5c094d99bd4 /src/mongo
parent526230bafa6e5a49f5783507734fba93486c19ae (diff)
downloadmongo-d3064df927c97abd402ee5768ab57667ed6849c9.tar.gz
SERVER-49380 Add API params to mongo shell
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/client/SConscript2
-rw-r--r--src/mongo/client/client_api_version_parameters.idl56
-rw-r--r--src/mongo/client/connection_string.h19
-rw-r--r--src/mongo/client/connection_string_connect.cpp24
-rw-r--r--src/mongo/client/dbclient_base.cpp47
-rw-r--r--src/mongo/client/dbclient_base.h15
-rw-r--r--src/mongo/client/dbclient_connection.cpp6
-rw-r--r--src/mongo/client/dbclient_connection.h3
-rw-r--r--src/mongo/client/dbclient_rs.cpp6
-rw-r--r--src/mongo/client/dbclient_rs.h3
-rw-r--r--src/mongo/client/mongo_uri.h5
-rw-r--r--src/mongo/client/mongo_uri_connect.cpp7
-rw-r--r--src/mongo/db/initialize_api_parameters.cpp11
-rw-r--r--src/mongo/db/initialize_api_parameters.h10
-rw-r--r--src/mongo/db/service_entry_point_common.cpp2
-rw-r--r--src/mongo/dbtests/mock/mock_conn_registry.cpp5
-rw-r--r--src/mongo/dbtests/mock/mock_conn_registry.h8
-rw-r--r--src/mongo/s/commands/strategy.cpp2
-rw-r--r--src/mongo/scripting/mozjs/mongo.cpp34
-rw-r--r--src/mongo/scripting/mozjs/mongo.h3
-rw-r--r--src/mongo/shell/db.js15
-rw-r--r--src/mongo/shell/mongo.js4
-rw-r--r--src/mongo/shell/mongo_main.cpp3
-rw-r--r--src/mongo/shell/shell_options.cpp25
-rw-r--r--src/mongo/shell/shell_options.h6
-rw-r--r--src/mongo/shell/shell_options.idl12
-rw-r--r--src/mongo/shell/shell_utils.cpp17
-rw-r--r--src/mongo/shell/utils.js3
28 files changed, 302 insertions, 51 deletions
diff --git a/src/mongo/client/SConscript b/src/mongo/client/SConscript
index bea8a3d755a..f6d531a737e 100644
--- a/src/mongo/client/SConscript
+++ b/src/mongo/client/SConscript
@@ -182,6 +182,7 @@ clientDriverEnv.Library(
'dbclient_base.cpp',
'dbclient_cursor.cpp',
'index_spec.cpp',
+ env.Idlc('client_api_version_parameters.idl')[0],
],
LIBDEPS=[
'$BUILD_DIR/mongo/db/dbmessage',
@@ -196,6 +197,7 @@ clientDriverEnv.Library(
'connection_string',
],
LIBDEPS_PRIVATE=[
+ '$BUILD_DIR/mongo/idl/idl_parser',
'$BUILD_DIR/mongo/util/net/ssl_manager',
],
)
diff --git a/src/mongo/client/client_api_version_parameters.idl b/src/mongo/client/client_api_version_parameters.idl
new file mode 100644
index 00000000000..26a89f01d56
--- /dev/null
+++ b/src/mongo/client/client_api_version_parameters.idl
@@ -0,0 +1,56 @@
+# Copyright (C) 2020-present MongoDB, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the Server Side Public License, version 1,
+# as published by MongoDB, Inc.
+#
+# 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
+# Server Side Public License for more details.
+#
+# You should have received a copy of the Server Side Public License
+# along with this program. If not, see
+# <http://www.mongodb.com/licensing/server-side-public-license>.
+#
+# 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 Server Side 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.
+
+global:
+ cpp_namespace: "mongo"
+
+imports:
+ - "mongo/idl/basic_types.idl"
+
+structs:
+
+ # Follow the MongoDB Drivers API for passing API Version parameters in clients. The drivers API
+ # is like MongoClient(uri, api={version: "1", strict: true, deprecationErrors: true}).
+
+ ClientAPIVersionParameters:
+ description: "Parser for Versioned API parameters passed to 'new Mongo()' in the mongo shell"
+ strict: true
+ fields:
+ version:
+ description: "The requested API version"
+ type: string
+ # The server requires the apiVersion parameter in commands, leave enforcement to the server.
+ optional: true
+ strict:
+ description: "Whether to restrict the connection to behaviors in the requested API version"
+ type: bool
+ optional: true
+ deprecationErrors:
+ description: "Whether to restrict the connection to non-deprecated behaviors in the
+ requested API version"
+ type: bool
+ optional: true
diff --git a/src/mongo/client/connection_string.h b/src/mongo/client/connection_string.h
index 493c916528f..67f62116d16 100644
--- a/src/mongo/client/connection_string.h
+++ b/src/mongo/client/connection_string.h
@@ -43,6 +43,7 @@
namespace mongo {
+class ClientAPIVersionParameters;
class DBClientBase;
class MongoURI;
@@ -121,10 +122,12 @@ public:
bool operator==(const ConnectionString& other) const;
bool operator!=(const ConnectionString& other) const;
- std::unique_ptr<DBClientBase> connect(StringData applicationName,
- std::string& errmsg,
- double socketTimeout = 0,
- const MongoURI* uri = nullptr) const;
+ std::unique_ptr<DBClientBase> connect(
+ StringData applicationName,
+ std::string& errmsg,
+ double socketTimeout = 0,
+ const MongoURI* uri = nullptr,
+ const ClientAPIVersionParameters* apiParameters = nullptr) const;
static StatusWith<ConnectionString> parse(const std::string& url);
@@ -147,9 +150,11 @@ public:
virtual ~ConnectionHook() {}
// Returns an alternative connection object for a string
- virtual std::unique_ptr<DBClientBase> connect(const ConnectionString& c,
- std::string& errmsg,
- double socketTimeout) = 0;
+ virtual std::unique_ptr<DBClientBase> connect(
+ const ConnectionString& c,
+ std::string& errmsg,
+ double socketTimeout,
+ const ClientAPIVersionParameters* apiParameters = nullptr) = 0;
};
static void setConnectionHook(ConnectionHook* hook) {
diff --git a/src/mongo/client/connection_string_connect.cpp b/src/mongo/client/connection_string_connect.cpp
index 498bf1db74d..735fc47d2af 100644
--- a/src/mongo/client/connection_string_connect.cpp
+++ b/src/mongo/client/connection_string_connect.cpp
@@ -46,10 +46,12 @@ namespace mongo {
Mutex ConnectionString::_connectHookMutex = MONGO_MAKE_LATCH();
ConnectionString::ConnectionHook* ConnectionString::_connectHook = nullptr;
-std::unique_ptr<DBClientBase> ConnectionString::connect(StringData applicationName,
- std::string& errmsg,
- double socketTimeout,
- const MongoURI* uri) const {
+std::unique_ptr<DBClientBase> ConnectionString::connect(
+ StringData applicationName,
+ std::string& errmsg,
+ double socketTimeout,
+ const MongoURI* uri,
+ const ClientAPIVersionParameters* apiParameters) const {
MongoURI newURI{};
if (uri) {
newURI = *uri;
@@ -58,7 +60,8 @@ std::unique_ptr<DBClientBase> ConnectionString::connect(StringData applicationNa
switch (_type) {
case MASTER: {
for (const auto& server : _servers) {
- auto c = std::make_unique<DBClientConnection>(true, 0, newURI);
+ auto c = std::make_unique<DBClientConnection>(
+ true, 0, newURI, DBClientConnection::HandshakeValidationHook(), apiParameters);
c->setSoTimeout(socketTimeout);
LOGV2_DEBUG(20109,
@@ -76,8 +79,12 @@ std::unique_ptr<DBClientBase> ConnectionString::connect(StringData applicationNa
}
case SET: {
- auto set = std::make_unique<DBClientReplicaSet>(
- _setName, _servers, applicationName, socketTimeout, std::move(newURI));
+ auto set = std::make_unique<DBClientReplicaSet>(_setName,
+ _servers,
+ applicationName,
+ socketTimeout,
+ std::move(newURI),
+ apiParameters);
if (!set->connect()) {
errmsg = "connect failed to replica set ";
errmsg += toString();
@@ -98,7 +105,8 @@ std::unique_ptr<DBClientBase> ConnectionString::connect(StringData applicationNa
_connectHook);
// Double-checked lock, since this will never be active during normal operation
- auto replacementConn = _connectHook->connect(*this, errmsg, socketTimeout);
+ auto replacementConn =
+ _connectHook->connect(*this, errmsg, socketTimeout, apiParameters);
LOGV2(20111,
"Replacing connection to {oldConnString} with {newConnString}",
diff --git a/src/mongo/client/dbclient_base.cpp b/src/mongo/client/dbclient_base.cpp
index 34408b9fddd..04748a525af 100644
--- a/src/mongo/client/dbclient_base.cpp
+++ b/src/mongo/client/dbclient_base.cpp
@@ -45,10 +45,12 @@
#include "mongo/bson/util/bson_extract.h"
#include "mongo/bson/util/builder.h"
#include "mongo/client/authenticate.h"
+#include "mongo/client/client_api_version_parameters_gen.h"
#include "mongo/client/constants.h"
#include "mongo/client/dbclient_cursor.h"
#include "mongo/config.h"
#include "mongo/db/commands.h"
+#include "mongo/db/initialize_api_parameters_gen.h"
#include "mongo/db/json.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/query/kill_cursors_gen.h"
@@ -194,6 +196,43 @@ DBClientBase* DBClientBase::runFireAndForgetCommand(OpMsgRequest request) {
return this;
}
+namespace {
+void appendAPIVersionParameters(BSONObjBuilder& bob,
+ const ClientAPIVersionParameters& apiParameters) {
+ if (!apiParameters.getVersion()) {
+ return;
+ }
+
+ bool hasVersion = false, hasStrict = false, hasDeprecationErrors = false;
+ BSONObjIterator i = bob.iterator();
+ while (i.more()) {
+ auto elem = i.next();
+ if (elem.fieldNameStringData() == APIParametersFromClient::kApiVersionFieldName) {
+ hasVersion = true;
+ } else if (elem.fieldNameStringData() == APIParametersFromClient::kApiStrictFieldName) {
+ hasStrict = true;
+ } else if (elem.fieldNameStringData() ==
+ APIParametersFromClient::kApiDeprecationErrorsFieldName) {
+ hasDeprecationErrors = true;
+ }
+ }
+
+ if (!hasVersion) {
+ bob.append(APIParametersFromClient::kApiVersionFieldName, *apiParameters.getVersion());
+ }
+
+ // Include apiStrict/apiDeprecationErrors if they are not boost::none.
+ if (!hasStrict && apiParameters.getStrict()) {
+ bob.append(APIParametersFromClient::kApiStrictFieldName, *apiParameters.getStrict());
+ }
+
+ if (!hasDeprecationErrors && apiParameters.getDeprecationErrors()) {
+ bob.append(APIParametersFromClient::kApiDeprecationErrorsFieldName,
+ *apiParameters.getDeprecationErrors());
+ }
+}
+} // namespace
+
std::pair<rpc::UniqueReply, DBClientBase*> DBClientBase::runCommandWithTarget(
OpMsgRequest request) {
// Make sure to reconnect if needed before building our request, since the request depends on
@@ -204,9 +243,13 @@ std::pair<rpc::UniqueReply, DBClientBase*> DBClientBase::runCommandWithTarget(
auto host = getServerAddress();
auto opCtx = haveClient() ? cc().getOperationContext() : nullptr;
- if (_metadataWriter) {
+
+ if (_metadataWriter || _apiParameters.getVersion()) {
BSONObjBuilder metadataBob(std::move(request.body));
- uassertStatusOK(_metadataWriter(opCtx, &metadataBob));
+ if (_metadataWriter) {
+ uassertStatusOK(_metadataWriter(opCtx, &metadataBob));
+ }
+ appendAPIVersionParameters(metadataBob, _apiParameters);
request.body = metadataBob.obj();
}
diff --git a/src/mongo/client/dbclient_base.h b/src/mongo/client/dbclient_base.h
index 92e4b8e245f..19bf9d2c417 100644
--- a/src/mongo/client/dbclient_base.h
+++ b/src/mongo/client/dbclient_base.h
@@ -34,6 +34,7 @@
#include "mongo/base/string_data.h"
#include "mongo/client/authenticate.h"
+#include "mongo/client/client_api_version_parameters_gen.h"
#include "mongo/client/connection_string.h"
#include "mongo/client/dbclient_cursor.h"
#include "mongo/client/index_spec.h"
@@ -109,11 +110,15 @@ class DBClientBase : public DBClientQueryInterface {
DBClientBase& operator=(const DBClientBase&) = delete;
public:
- DBClientBase()
+ DBClientBase(const ClientAPIVersionParameters* apiParameters = nullptr)
: _logLevel(logv2::LogSeverity::Log()),
_connectionId(ConnectionIdSequence.fetchAndAdd(1)),
_cachedAvailableOptions((enum QueryOptions)0),
- _haveCachedAvailableOptions(false) {}
+ _haveCachedAvailableOptions(false) {
+ if (apiParameters) {
+ _apiParameters = *apiParameters;
+ }
+ }
virtual ~DBClientBase() {}
@@ -770,6 +775,10 @@ public:
virtual const SSLConfiguration* getSSLConfiguration() = 0;
#endif
+ const ClientAPIVersionParameters& getApiParameters() const {
+ return _apiParameters;
+ }
+
protected:
/** if the result of a command is ok*/
bool isOk(const BSONObj&);
@@ -836,6 +845,8 @@ private:
// The operationTime associated with the last command handles by the client.
// TODO(SERVER-49791): Implement proper tracking of operationTime.
Timestamp _lastOperationTime;
+
+ ClientAPIVersionParameters _apiParameters;
}; // DBClientBase
BSONElement getErrField(const BSONObj& result);
diff --git a/src/mongo/client/dbclient_connection.cpp b/src/mongo/client/dbclient_connection.cpp
index 7ae8c5ca793..66714d742de 100644
--- a/src/mongo/client/dbclient_connection.cpp
+++ b/src/mongo/client/dbclient_connection.cpp
@@ -678,8 +678,10 @@ unsigned long long DBClientConnection::query(std::function<void(DBClientCursorBa
DBClientConnection::DBClientConnection(bool _autoReconnect,
double so_timeout,
MongoURI uri,
- const HandshakeValidationHook& hook)
- : autoReconnect(_autoReconnect),
+ const HandshakeValidationHook& hook,
+ const ClientAPIVersionParameters* apiParameters)
+ : DBClientBase(apiParameters),
+ autoReconnect(_autoReconnect),
_autoReconnectBackoff(Seconds(1), Seconds(2)),
_hook(hook),
_uri(std::move(uri)) {
diff --git a/src/mongo/client/dbclient_connection.h b/src/mongo/client/dbclient_connection.h
index db6c0c2a567..f5408f839be 100644
--- a/src/mongo/client/dbclient_connection.h
+++ b/src/mongo/client/dbclient_connection.h
@@ -95,7 +95,8 @@ public:
DBClientConnection(bool _autoReconnect = false,
double so_timeout = 0,
MongoURI uri = {},
- const HandshakeValidationHook& hook = HandshakeValidationHook());
+ const HandshakeValidationHook& hook = HandshakeValidationHook(),
+ const ClientAPIVersionParameters* apiParameters = nullptr);
virtual ~DBClientConnection() {
_numConnections.fetchAndAdd(-1);
diff --git a/src/mongo/client/dbclient_rs.cpp b/src/mongo/client/dbclient_rs.cpp
index 0359251d9fa..b9871482aac 100644
--- a/src/mongo/client/dbclient_rs.cpp
+++ b/src/mongo/client/dbclient_rs.cpp
@@ -133,8 +133,10 @@ DBClientReplicaSet::DBClientReplicaSet(const string& name,
const vector<HostAndPort>& servers,
StringData applicationName,
double so_timeout,
- MongoURI uri)
- : _setName(name),
+ MongoURI uri,
+ const ClientAPIVersionParameters* apiParameters)
+ : DBClientBase(apiParameters),
+ _setName(name),
_applicationName(applicationName.toString()),
_so_timeout(so_timeout),
_uri(std::move(uri)) {
diff --git a/src/mongo/client/dbclient_rs.h b/src/mongo/client/dbclient_rs.h
index 4710a300c5d..e86cac71ae9 100644
--- a/src/mongo/client/dbclient_rs.h
+++ b/src/mongo/client/dbclient_rs.h
@@ -67,7 +67,8 @@ public:
const std::vector<HostAndPort>& servers,
StringData applicationName,
double so_timeout = 0,
- MongoURI uri = {});
+ MongoURI uri = {},
+ const ClientAPIVersionParameters* apiParameters = nullptr);
/**
* Returns false if no member of the set were reachable. This object
diff --git a/src/mongo/client/mongo_uri.h b/src/mongo/client/mongo_uri.h
index 3d155e1123c..26546ed7af5 100644
--- a/src/mongo/client/mongo_uri.h
+++ b/src/mongo/client/mongo_uri.h
@@ -46,6 +46,8 @@
namespace mongo {
+class ClientAPIVersionParameters;
+
/**
* Encode a string for embedding in a URI.
* Replaces reserved bytes with %xx sequences.
@@ -149,7 +151,8 @@ public:
DBClientBase* connect(StringData applicationName,
std::string& errmsg,
- boost::optional<double> socketTimeoutSecs = boost::none) const;
+ boost::optional<double> socketTimeoutSecs = boost::none,
+ const ClientAPIVersionParameters* apiParameters = nullptr) const;
const std::string& getUser() const {
return _user;
diff --git a/src/mongo/client/mongo_uri_connect.cpp b/src/mongo/client/mongo_uri_connect.cpp
index ae556d20aa9..34144bc0b66 100644
--- a/src/mongo/client/mongo_uri_connect.cpp
+++ b/src/mongo/client/mongo_uri_connect.cpp
@@ -39,7 +39,8 @@ namespace mongo {
DBClientBase* MongoURI::connect(StringData applicationName,
std::string& errmsg,
- boost::optional<double> socketTimeoutSecs) const {
+ boost::optional<double> socketTimeoutSecs,
+ const ClientAPIVersionParameters* apiParameters) const {
OptionsMap::const_iterator it = _options.find("socketTimeoutMS");
if (it != _options.end() && !socketTimeoutSecs) {
try {
@@ -50,8 +51,8 @@ DBClientBase* MongoURI::connect(StringData applicationName,
}
}
- auto ret = std::unique_ptr<DBClientBase>(
- _connectString.connect(applicationName, errmsg, socketTimeoutSecs.value_or(0.0), this));
+ auto ret = std::unique_ptr<DBClientBase>(_connectString.connect(
+ applicationName, errmsg, socketTimeoutSecs.value_or(0.0), this, apiParameters));
if (!ret) {
return nullptr;
}
diff --git a/src/mongo/db/initialize_api_parameters.cpp b/src/mongo/db/initialize_api_parameters.cpp
index a0e0a95220b..84ef1ce568a 100644
--- a/src/mongo/db/initialize_api_parameters.cpp
+++ b/src/mongo/db/initialize_api_parameters.cpp
@@ -31,14 +31,17 @@
namespace mongo {
-const APIParametersFromClient initializeAPIParameters(const BSONObj& requestBody,
+const APIParametersFromClient initializeAPIParameters(OperationContext* opCtx,
+ const BSONObj& requestBody,
Command* command) {
-
auto apiParamsFromClient =
APIParametersFromClient::parse("APIParametersFromClient"_sd, requestBody);
- if (gRequireApiVersion.load()) {
- uassert(498870, "Missing apiVersion parameter", apiParamsFromClient.getApiVersion());
+ if (gRequireApiVersion.load() && !opCtx->getClient()->isInDirectClient()) {
+ uassert(
+ 498870,
+ "The apiVersion parameter is required, please configure your MongoClient's API version",
+ apiParamsFromClient.getApiVersion());
}
if (apiParamsFromClient.getApiDeprecationErrors() || apiParamsFromClient.getApiStrict()) {
diff --git a/src/mongo/db/initialize_api_parameters.h b/src/mongo/db/initialize_api_parameters.h
index 9ca8bec10b7..b255b6800d7 100644
--- a/src/mongo/db/initialize_api_parameters.h
+++ b/src/mongo/db/initialize_api_parameters.h
@@ -41,7 +41,9 @@ namespace mongo {
* This function parses a command's API Version parameters from a request and stores the apiVersion,
* apiStrict, and apiDeprecationErrors fields.
*/
-const APIParametersFromClient initializeAPIParameters(const BSONObj& requestBody, Command* command);
+const APIParametersFromClient initializeAPIParameters(OperationContext* opCtx,
+ const BSONObj& requestBody,
+ Command* command);
/**
* Decorates operation context with methods to retrieve apiVersion, apiStrict, and
@@ -65,7 +67,7 @@ public:
}
void setAPIVersion(StringData apiVersion) {
- _apiVersion = apiVersion;
+ _apiVersion = apiVersion.toString();
}
const bool getAPIStrict() const {
@@ -92,8 +94,10 @@ public:
_paramsPassed = paramsPassed;
}
+ BSONObj toBSON() const;
+
private:
- StringData _apiVersion;
+ std::string _apiVersion;
bool _apiStrict;
bool _apiDeprecationErrors;
bool _paramsPassed;
diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp
index 006f1488f2f..81ab2961805 100644
--- a/src/mongo/db/service_entry_point_common.cpp
+++ b/src/mongo/db/service_entry_point_common.cpp
@@ -936,7 +936,7 @@ void execCommandDatabase(OperationContext* opCtx,
(opCtx->getClient()->session()->getTags() & transport::Session::kInternalClient);
try {
- const auto apiParamsFromClient = initializeAPIParameters(request.body, command);
+ const auto apiParamsFromClient = initializeAPIParameters(opCtx, request.body, command);
Client* client = opCtx->getClient();
{
diff --git a/src/mongo/dbtests/mock/mock_conn_registry.cpp b/src/mongo/dbtests/mock/mock_conn_registry.cpp
index 1c9e52b018f..43e4e4b2311 100644
--- a/src/mongo/dbtests/mock/mock_conn_registry.cpp
+++ b/src/mongo/dbtests/mock/mock_conn_registry.cpp
@@ -89,7 +89,10 @@ MockConnRegistry::MockConnHook::MockConnHook(MockConnRegistry* registry) : _regi
MockConnRegistry::MockConnHook::~MockConnHook() {}
std::unique_ptr<mongo::DBClientBase> MockConnRegistry::MockConnHook::connect(
- const ConnectionString& connString, std::string& errmsg, double socketTimeout) {
+ const ConnectionString& connString,
+ std::string& errmsg,
+ double socketTimeout,
+ const ClientAPIVersionParameters* apiParameters) {
const string hostName(connString.toString());
auto conn = _registry->connect(hostName);
diff --git a/src/mongo/dbtests/mock/mock_conn_registry.h b/src/mongo/dbtests/mock/mock_conn_registry.h
index feb8eb86517..57bcae8a44f 100644
--- a/src/mongo/dbtests/mock/mock_conn_registry.h
+++ b/src/mongo/dbtests/mock/mock_conn_registry.h
@@ -99,9 +99,11 @@ private:
MockConnHook(MockConnRegistry* registry);
~MockConnHook();
- std::unique_ptr<mongo::DBClientBase> connect(const mongo::ConnectionString& connString,
- std::string& errmsg,
- double socketTimeout);
+ std::unique_ptr<mongo::DBClientBase> connect(
+ const mongo::ConnectionString& connString,
+ std::string& errmsg,
+ double socketTimeout,
+ const ClientAPIVersionParameters* apiParameters = nullptr) override;
private:
MockConnRegistry* _registry;
diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp
index ba7220b3839..644c10e6bcb 100644
--- a/src/mongo/s/commands/strategy.cpp
+++ b/src/mongo/s/commands/strategy.cpp
@@ -372,7 +372,7 @@ void runCommand(OperationContext* opCtx,
auto wc = uassertStatusOK(WriteConcernOptions::extractWCFromCommand(request.body));
Client* client = opCtx->getClient();
- auto const apiParamsFromClient = initializeAPIParameters(request.body, command);
+ auto const apiParamsFromClient = initializeAPIParameters(opCtx, request.body, command);
auto& readConcernArgs = repl::ReadConcernArgs::get(opCtx);
Status readConcernParseStatus = Status::OK();
diff --git a/src/mongo/scripting/mozjs/mongo.cpp b/src/mongo/scripting/mozjs/mongo.cpp
index 9d13c551017..6849e636391 100644
--- a/src/mongo/scripting/mozjs/mongo.cpp
+++ b/src/mongo/scripting/mozjs/mongo.cpp
@@ -34,6 +34,7 @@
#include <memory>
#include "mongo/bson/simple_bsonelement_comparator.h"
+#include "mongo/client/client_api_version_parameters_gen.h"
#include "mongo/client/dbclient_base.h"
#include "mongo/client/dbclient_rs.h"
#include "mongo/client/global_conn_pool.h"
@@ -87,6 +88,7 @@ const JSFunctionSpec MongoBase::methods[] = {
MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(getMaxWireVersion, MongoExternalInfo),
MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(isReplicaSetMember, MongoExternalInfo),
MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(isMongos, MongoExternalInfo),
+ MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(getApiParameters, MongoExternalInfo),
MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(_startSession, MongoExternalInfo),
JS_FS_END,
};
@@ -815,6 +817,7 @@ void setEncryptedDBClientCallback(EncryptedDBClientCallback* callback) {
encryptedDBClientCallback = callback;
}
+// "new Mongo(uri, encryptedDBClientCallback, {options...})"
void MongoExternalInfo::construct(JSContext* cx, JS::CallArgs args) {
auto scope = getScope(cx);
@@ -826,9 +829,33 @@ void MongoExternalInfo::construct(JSContext* cx, JS::CallArgs args) {
auto cs = uassertStatusOK(MongoURI::parse(host));
+ ClientAPIVersionParameters apiParameters;
+ if (args.length() > 2 && !args.get(2).isUndefined()) {
+ uassert(4938000,
+ str::stream() << "the 'options' parameter to Mongo() must be an object",
+ args.get(2).isObject());
+ auto options = ValueWriter(cx, args.get(2)).toBSON();
+ if (options.hasField("api")) {
+ uassert(4938001,
+ "the 'api' option for Mongo() must be an object",
+ options["api"].isABSONObj());
+ apiParameters = ClientAPIVersionParameters::parse(IDLParserErrorContext("api"_sd),
+ options["api"].Obj());
+ if (apiParameters.getDeprecationErrors().value_or(false) ||
+ apiParameters.getStrict().value_or(false)) {
+ uassert(4938002,
+ "the 'api' option for Mongo() must include 'version' if it includes "
+ "'strict' or "
+ "'deprecationErrors'",
+ apiParameters.getVersion());
+ }
+ }
+ }
+
boost::optional<std::string> appname = cs.getAppName();
std::string errmsg;
- std::unique_ptr<DBClientBase> conn(cs.connect(appname.value_or("MongoDB Shell"), errmsg));
+ std::unique_ptr<DBClientBase> conn(
+ cs.connect(appname.value_or("MongoDB Shell"), errmsg, boost::none, &apiParameters));
if (!conn.get()) {
uasserted(ErrorCodes::InternalError, errmsg);
@@ -885,6 +912,11 @@ void MongoBase::Functions::isMongos::call(JSContext* cx, JS::CallArgs args) {
args.rval().setBoolean(conn->isMongos());
}
+void MongoBase::Functions::getApiParameters::call(JSContext* cx, JS::CallArgs args) {
+ auto conn = getConnection(args);
+ ValueReader(cx, args.rval()).fromBSON(conn->getApiParameters().toBSON(), nullptr, false);
+}
+
void MongoBase::Functions::_startSession::call(JSContext* cx, JS::CallArgs args) {
auto client =
static_cast<std::shared_ptr<DBClientBase>*>(JS_GetPrivate(args.thisv().toObjectOrNull()));
diff --git a/src/mongo/scripting/mozjs/mongo.h b/src/mongo/scripting/mozjs/mongo.h
index 15020b365b5..2d9283fe8af 100644
--- a/src/mongo/scripting/mozjs/mongo.h
+++ b/src/mongo/scripting/mozjs/mongo.h
@@ -78,10 +78,11 @@ struct MongoBase : public BaseInfo {
MONGO_DECLARE_JS_FUNCTION(getMaxWireVersion);
MONGO_DECLARE_JS_FUNCTION(isReplicaSetMember);
MONGO_DECLARE_JS_FUNCTION(isMongos);
+ MONGO_DECLARE_JS_FUNCTION(getApiParameters);
MONGO_DECLARE_JS_FUNCTION(_startSession);
};
- static const JSFunctionSpec methods[27];
+ static const JSFunctionSpec methods[28];
static const char* const className;
static const unsigned classFlags = JSCLASS_HAS_PRIVATE;
diff --git a/src/mongo/shell/db.js b/src/mongo/shell/db.js
index dc492dd6ffc..44c9a9b24d9 100644
--- a/src/mongo/shell/db.js
+++ b/src/mongo/shell/db.js
@@ -197,6 +197,16 @@ DB.prototype.adminCommand = function(obj, extra) {
DB.prototype._adminCommand = DB.prototype.adminCommand; // alias old name
+DB.prototype._runCommandWithoutApiStrict = function(command) {
+ let commandWithoutApiStrict = Object.assign({}, command);
+ if (this.getMongo().getApiParameters().strict) {
+ // Permit this command invocation, even if it's not in the requested API version.
+ commandWithoutApiStrict["apiStrict"] = false;
+ }
+
+ return this.runCommand(commandWithoutApiStrict);
+};
+
DB.prototype._runAggregate = function(cmdObj, aggregateOptions) {
assert(cmdObj.pipeline instanceof Array, "cmdObj must contain a 'pipeline' array");
assert(cmdObj.aggregate !== undefined, "cmdObj must contain 'aggregate' field");
@@ -1104,7 +1114,8 @@ DB.prototype.printSecondaryReplicationInfo = function() {
var L = this.getSiblingDB("local");
if (L.system.replset.count() != 0) {
- var status = this.adminCommand({'replSetGetStatus': 1});
+ const status =
+ this.getSiblingDB('admin')._runCommandWithoutApiStrict({'replSetGetStatus': 1});
primary = getPrimary(status.members);
if (primary) {
startOptimeDate = primary.optimeDate;
@@ -1126,7 +1137,7 @@ DB.prototype.printSecondaryReplicationInfo = function() {
};
DB.prototype.serverBuildInfo = function() {
- return this._adminCommand("buildinfo");
+ return this.getSiblingDB("admin")._runCommandWithoutApiStrict({buildinfo: 1});
};
// Used to trim entries from the metrics.commands that have never been executed
diff --git a/src/mongo/shell/mongo.js b/src/mongo/shell/mongo.js
index 6790fcce8d6..71f6c707810 100644
--- a/src/mongo/shell/mongo.js
+++ b/src/mongo/shell/mongo.js
@@ -308,7 +308,7 @@ Mongo.prototype.getReadConcern = function() {
return this._readConcernLevel;
};
-connect = function(url, user, pass) {
+connect = function(url, user, pass, apiParameters) {
if (url instanceof MongoURI) {
user = url.user;
pass = url.password;
@@ -359,7 +359,7 @@ connect = function(url, user, pass) {
}
chatty("connecting to: " + safeURL);
try {
- var m = new Mongo(url);
+ var m = new Mongo(url, undefined /* encryptedDBClientCallback */, apiParameters);
} catch (e) {
var dest;
if (url.indexOf(".query.mongodb.net") != -1) {
diff --git a/src/mongo/shell/mongo_main.cpp b/src/mongo/shell/mongo_main.cpp
index 3343a61075c..ecdca584ab1 100644
--- a/src/mongo/shell/mongo_main.cpp
+++ b/src/mongo/shell/mongo_main.cpp
@@ -818,7 +818,8 @@ int mongo_main(int argc, char* argv[]) {
ss << "__quiet = true;" << std::endl;
}
- ss << "db = connect( \"" << parsedURI.canonicalizeURIAsString() << "\");" << std::endl;
+ ss << "db = connect( \"" << parsedURI.canonicalizeURIAsString()
+ << "\", null, null, {api: " << getApiParametersJSON() << "});" << std::endl;
if (shellGlobalParams.shouldRetryWrites || parsedURI.getRetryWrites()) {
// If the --retryWrites cmdline argument or retryWrites URI param was specified,
diff --git a/src/mongo/shell/shell_options.cpp b/src/mongo/shell/shell_options.cpp
index 307e6301f48..d04885d722d 100644
--- a/src/mongo/shell/shell_options.cpp
+++ b/src/mongo/shell/shell_options.cpp
@@ -39,6 +39,7 @@
#include "mongo/base/status.h"
#include "mongo/bson/util/builder.h"
+#include "mongo/client/client_api_version_parameters_gen.h"
#include "mongo/client/mongo_uri.h"
#include "mongo/config.h"
#include "mongo/db/auth/sasl_command_constants.h"
@@ -320,6 +321,13 @@ Status storeMongoShellOptions(const moe::Environment& params,
}
}
+ // Future API versions may require logic changes in the shell, so ban them for now.
+ if (!shellGlobalParams.apiVersion.empty() && shellGlobalParams.apiVersion != "1") {
+ uasserted(4938003,
+ str::stream() << "Bad value --apiVersion '" << shellGlobalParams.apiVersion
+ << "', only API Version 1 is supported");
+ }
+
if (params.count("setShellParameter")) {
auto ssp = params["setShellParameter"].as<std::map<std::string, std::string>>();
auto map = ServerParameterSet::getGlobal()->getMap();
@@ -348,4 +356,21 @@ Status storeMongoShellOptions(const moe::Environment& params,
return Status::OK();
}
+std::string getApiParametersJSON() {
+ BSONObjBuilder bob;
+ if (!shellGlobalParams.apiVersion.empty()) {
+ bob.append(ClientAPIVersionParameters::kVersionFieldName, shellGlobalParams.apiVersion);
+ }
+
+ if (shellGlobalParams.apiStrict) {
+ bob.append(ClientAPIVersionParameters::kStrictFieldName, true);
+ }
+
+ if (shellGlobalParams.apiDeprecationErrors) {
+ bob.append(ClientAPIVersionParameters::kDeprecationErrorsFieldName, true);
+ }
+
+ return bob.done().jsonString();
+}
+
} // namespace mongo
diff --git a/src/mongo/shell/shell_options.h b/src/mongo/shell/shell_options.h
index 45ac4e55455..2d5582e4ffc 100644
--- a/src/mongo/shell/shell_options.h
+++ b/src/mongo/shell/shell_options.h
@@ -68,6 +68,10 @@ struct ShellGlobalParams {
std::string script;
+ std::string apiVersion;
+ bool apiStrict;
+ bool apiDeprecationErrors;
+
bool autoKillOp = false;
bool useWriteCommandsDefault = true;
@@ -98,4 +102,6 @@ bool handlePreValidationMongoShellOptions(const moe::Environment& params,
Status storeMongoShellOptions(const moe::Environment& params, const std::vector<std::string>& args);
void redactPasswordOptions(int argc, char** argv);
+
+std::string getApiParametersJSON();
} // namespace mongo
diff --git a/src/mongo/shell/shell_options.idl b/src/mongo/shell/shell_options.idl
index c56a951b2ee..49f402b42e3 100644
--- a/src/mongo/shell/shell_options.idl
+++ b/src/mongo/shell/shell_options.idl
@@ -78,6 +78,18 @@ configs:
arg_vartype: String
cpp_varname: shellGlobalParams.script
+ "apiVersion":
+ description: "set the MongoDB API version"
+ arg_vartype: String
+ cpp_varname: shellGlobalParams.apiVersion
+ "apiStrict":
+ description: "disable all features not included in the MongoDB Versioned API"
+ arg_vartype: Switch
+ cpp_varname: shellGlobalParams.apiStrict
+ "apiDeprecationErrors":
+ description: "disable all features deprecated in the MongoDB Versioned API"
+ arg_vartype: Switch
+ cpp_varname: shellGlobalParams.apiDeprecationErrors
"objcheck":
description: "inspect client data for validity on receipt"
arg_vartype: Switch
diff --git a/src/mongo/shell/shell_utils.cpp b/src/mongo/shell/shell_utils.cpp
index ae93862b113..4355bf7afd1 100644
--- a/src/mongo/shell/shell_utils.cpp
+++ b/src/mongo/shell/shell_utils.cpp
@@ -445,6 +445,12 @@ BSONObj shouldUseImplicitSessions(const BSONObj&, void* data) {
return BSON("" << shellGlobalParams.shouldUseImplicitSessions);
}
+BSONObj apiParameters(const BSONObj&, void* data) {
+ return BSON("" << BSON("apiVersion" << shellGlobalParams.apiVersion << "apiStrict"
+ << shellGlobalParams.apiStrict << "apiDeprecationErrors"
+ << shellGlobalParams.apiDeprecationErrors));
+}
+
BSONObj interpreterVersion(const BSONObj& a, void* data) {
uassert(16453, "interpreterVersion accepts no arguments", a.nFields() == 0);
return BSON("" << getGlobalScriptEngine()->getInterpreterVersionString());
@@ -496,6 +502,7 @@ void initScope(Scope& scope) {
scope.injectNative("_readMode", readMode);
scope.injectNative("_shouldRetryWrites", shouldRetryWrites);
scope.injectNative("_shouldUseImplicitSessions", shouldUseImplicitSessions);
+ scope.injectNative("_apiParameters", apiParameters);
scope.externalSetup();
mongo::shell_utils::installShellUtils(scope);
scope.execSetup(JSFiles::servers);
@@ -540,7 +547,15 @@ ConnectionRegistry::ConnectionRegistry() = default;
void ConnectionRegistry::registerConnection(DBClientBase& client, StringData uri) {
BSONObj info;
- if (client.runCommand("admin", BSON("whatsmyuri" << 1), info)) {
+ BSONObj command;
+ // If apiStrict is set override it, whatsmyuri is not in the Versioned API.
+ if (client.getApiParameters().getStrict()) {
+ command = BSON("whatsmyuri" << 1 << "apiStrict" << false);
+ } else {
+ command = BSON("whatsmyuri" << 1);
+ }
+
+ if (client.runCommand("admin", command, info)) {
stdx::lock_guard<Latch> lk(_mutex);
_connectionUris[uri.toString()].insert(info["you"].str());
}
diff --git a/src/mongo/shell/utils.js b/src/mongo/shell/utils.js
index b84250ad598..50f443e82ee 100644
--- a/src/mongo/shell/utils.js
+++ b/src/mongo/shell/utils.js
@@ -1016,7 +1016,8 @@ shellHelper.show = function(what) {
dbDeclared = false;
}
if (dbDeclared) {
- var res = db.adminCommand({getLog: "startupWarnings"});
+ var res =
+ db.getSiblingDB("admin")._runCommandWithoutApiStrict({getLog: "startupWarnings"});
if (res.ok) {
if (res.log.length == 0) {
return "";