diff options
author | Henrik Edin <henrik.edin@mongodb.com> | 2018-04-03 10:29:48 -0400 |
---|---|---|
committer | Henrik Edin <henrik.edin@mongodb.com> | 2018-04-10 10:41:36 -0400 |
commit | cb2b7de230a656986553e99f4a9f3a64f6be8a1e (patch) | |
tree | cf185762899c6c513aa66eb814fa5688b6cdac82 | |
parent | fb9aed12d2ab29884f2567ae9e60e021790823bd (diff) | |
download | mongo-cb2b7de230a656986553e99f4a9f3a64f6be8a1e.tar.gz |
SERVER-32487 Embedded CAPI takes a yaml config string instead of argc and argv.
-rw-r--r-- | src/mongo/client/embedded/SConscript | 10 | ||||
-rw-r--r-- | src/mongo/client/embedded/embedded.cpp | 10 | ||||
-rw-r--r-- | src/mongo/client/embedded/embedded.h | 2 | ||||
-rw-r--r-- | src/mongo/client/embedded/embedded_options.cpp | 6 | ||||
-rw-r--r-- | src/mongo/client/embedded/embedded_options_parser_init.cpp | 57 | ||||
-rw-r--r-- | src/mongo/client/embedded/embedded_transport_layer_test.cpp | 21 | ||||
-rw-r--r-- | src/mongo/client/embedded/libmongodbcapi.cpp | 35 | ||||
-rw-r--r-- | src/mongo/client/embedded/libmongodbcapi.h | 10 | ||||
-rw-r--r-- | src/mongo/client/embedded/libmongodbcapi_test.cpp | 24 | ||||
-rw-r--r-- | src/mongo/util/options_parser/options_parser.cpp | 66 | ||||
-rw-r--r-- | src/mongo/util/options_parser/options_parser.h | 13 |
11 files changed, 178 insertions, 76 deletions
diff --git a/src/mongo/client/embedded/SConscript b/src/mongo/client/embedded/SConscript index 369ff49dcb0..1130e37cdc6 100644 --- a/src/mongo/client/embedded/SConscript +++ b/src/mongo/client/embedded/SConscript @@ -41,7 +41,6 @@ env.Library( '$BUILD_DIR/mongo/db/storage/storage_engine_lock_file', '$BUILD_DIR/mongo/db/storage/storage_engine_metadata', '$BUILD_DIR/mongo/db/storage/storage_options', - '$BUILD_DIR/mongo/util/options_parser/options_parser_init', 'service_entry_point_embedded', ], ) @@ -61,6 +60,7 @@ env.Library( source=[ 'embedded_options.cpp', 'embedded_options_init.cpp', + 'embedded_options_parser_init.cpp', ], LIBDEPS=[ '$BUILD_DIR/mongo/db/server_options', @@ -147,7 +147,9 @@ if get_option('install-mode') == 'hygienic': ], ) -capiTest = env.Program( +capiTestEnv = env.Clone() +capiTestEnv.InjectThirdPartyIncludePaths(libraries=['yaml']) +capiTest = capiTestEnv.Program( target='mongo_embedded_capi_test', source=[ 'libmongodbcapi_test.cpp', @@ -204,7 +206,9 @@ if get_option('install-mode') == 'hygienic': ], ) -embeddedTransportTest = env.Program( +embeddedTransportTestEnv = env.Clone() +embeddedTransportTestEnv.InjectThirdPartyIncludePaths(libraries=['yaml']) +embeddedTransportTest = embeddedTransportTestEnv.Program( target='mongo_embedded_transport_test', source=[ 'embedded_transport_layer_test.cpp', diff --git a/src/mongo/client/embedded/embedded.cpp b/src/mongo/client/embedded/embedded.cpp index 955b221d0f3..328217bd157 100644 --- a/src/mongo/client/embedded/embedded.cpp +++ b/src/mongo/client/embedded/embedded.cpp @@ -167,11 +167,17 @@ void shutdown(ServiceContext* srvContext) { } -ServiceContext* initialize(int argc, char* argv[], char** envp) { +ServiceContext* initialize(const char* yaml_config) { srand(static_cast<unsigned>(curTimeMicros64())); setGlobalServiceContext(createServiceContext()); - Status status = mongo::runGlobalInitializers(argc, argv, envp, getGlobalServiceContext()); + + // yaml_config is passed to the options parser through the argc/argv interface that already + // existed. If it is nullptr then use 0 count which will be interpreted as empty string. + const char* argv[2] = {yaml_config, nullptr}; + + Status status = + mongo::runGlobalInitializers(yaml_config ? 1 : 0, argv, nullptr, getGlobalServiceContext()); uassertStatusOKWithContext(status, "Global initilization failed"); Client::initThread("initandlisten"); diff --git a/src/mongo/client/embedded/embedded.h b/src/mongo/client/embedded/embedded.h index 714c6c1c90a..8601b4e709a 100644 --- a/src/mongo/client/embedded/embedded.h +++ b/src/mongo/client/embedded/embedded.h @@ -34,7 +34,7 @@ namespace mongo { class ServiceContext; namespace embedded { -ServiceContext* initialize(int argc, char* argv[], char** envp); +ServiceContext* initialize(const char* yaml_config); void shutdown(ServiceContext* serviceContext); } // namespace embedded } // namespace mongo diff --git a/src/mongo/client/embedded/embedded_options.cpp b/src/mongo/client/embedded/embedded_options.cpp index f47f771d3f7..8d1096a2300 100644 --- a/src/mongo/client/embedded/embedded_options.cpp +++ b/src/mongo/client/embedded/embedded_options.cpp @@ -52,8 +52,10 @@ Status addOptions(optionenvironment::OptionSection* options) { moe::OptionSection storage_options("Storage options"); - storage_options.addOptionChaining( - "storage.engine", "storageEngine", moe::String, "what storage engine to use"); + storage_options + .addOptionChaining( + "storage.engine", "storageEngine", moe::String, "what storage engine to use") + .setDefault(optionenvironment::Value("mobile")); #ifdef _WIN32 boost::filesystem::path currentPath = boost::filesystem::current_path(); diff --git a/src/mongo/client/embedded/embedded_options_parser_init.cpp b/src/mongo/client/embedded/embedded_options_parser_init.cpp new file mode 100644 index 00000000000..1416556828a --- /dev/null +++ b/src/mongo/client/embedded/embedded_options_parser_init.cpp @@ -0,0 +1,57 @@ +/** + * Copyright (C) 2018 MongoDB 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 <http://www.gnu.org/licenses/>. + * + * 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/util/options_parser/startup_options.h" + +#include <iostream> + +#include "mongo/util/exit_code.h" +#include "mongo/util/options_parser/option_description.h" +#include "mongo/util/options_parser/option_section.h" +#include "mongo/util/options_parser/options_parser.h" +#include "mongo/util/options_parser/startup_option_init.h" +#include "mongo/util/quick_exit.h" + +namespace mongo { +namespace optionenvironment { + +MONGO_STARTUP_OPTIONS_PARSE(StartupOptions)(InitializerContext* context) { + // Embedded uses a YAML config passed in argv to reuse the existing interface, extract it from + // the first element otherwise use empty string. + std::string config = !context->args().empty() ? context->args()[0] : ""; + + OptionsParser parser; + Status ret = + parser.runConfigFile(startupOptions, config, context->env(), &startupOptionsParsed); + uassertStatusOKWithContext(ret, "Options parsing failed."); + + return Status::OK(); +} + +} // namespace optionenvironment +} // namespace mongo diff --git a/src/mongo/client/embedded/embedded_transport_layer_test.cpp b/src/mongo/client/embedded/embedded_transport_layer_test.cpp index df09479c32c..8b5c262a7f2 100644 --- a/src/mongo/client/embedded/embedded_transport_layer_test.cpp +++ b/src/mongo/client/embedded/embedded_transport_layer_test.cpp @@ -30,6 +30,7 @@ #include <mongoc.h> #include <set> +#include <yaml-cpp/yaml.h> #include "mongo/client/embedded/functions_for_test.h" #include "mongo/client/embedded/libmongodbcapi.h" @@ -55,13 +56,19 @@ protected: if (!globalTempDir) { globalTempDir = mongo::stdx::make_unique<mongo::unittest::TempDir>("embedded_mongo"); } - int argc = 5; - const char* argv[] = {"mongo_embedded_transport_layer_test", - "--storageEngine", - "mobile", - "--dbpath", - globalTempDir->path().c_str()}; - db_handle = libmongodbcapi_db_new(argc, argv, nullptr); + + YAML::Emitter yaml; + yaml << YAML::BeginMap; + + yaml << YAML::Key << "storage"; + yaml << YAML::Value << YAML::BeginMap; + yaml << YAML::Key << "dbPath"; + yaml << YAML::Value << globalTempDir->path(); + yaml << YAML::EndMap; // storage + + yaml << YAML::EndMap; + + db_handle = libmongodbcapi_db_new(yaml.c_str()); cd_client = embedded_mongoc_client_new(db_handle); mongoc_client_set_error_api(cd_client, 2); diff --git a/src/mongo/client/embedded/libmongodbcapi.cpp b/src/mongo/client/embedded/libmongodbcapi.cpp index 233422cd34b..0b38c244e0a 100644 --- a/src/mongo/client/embedded/libmongodbcapi.cpp +++ b/src/mongo/client/embedded/libmongodbcapi.cpp @@ -56,11 +56,6 @@ struct libmongodbcapi_db { mongo::stdx::unordered_map<libmongodbcapi_client*, std::unique_ptr<libmongodbcapi_client>> open_clients; std::unique_ptr<mongo::transport::TransportLayerMock> transportLayer; - - std::vector<std::unique_ptr<char[]>> argvStorage; - std::vector<char*> argvPointers; - std::vector<std::unique_ptr<char[]>> envpStorage; - std::vector<char*> envpPointers; }; struct libmongodbcapi_client { libmongodbcapi_client(libmongodbcapi_db* db) : parent_db(db) {} @@ -82,7 +77,7 @@ bool libraryInitialized_ = false; libmongodbcapi_db* global_db = nullptr; thread_local int last_error = LIBMONGODB_CAPI_SUCCESS; -libmongodbcapi_db* db_new(int argc, const char** argv, const char** envp) noexcept try { +libmongodbcapi_db* db_new(const char* yaml_config) noexcept try { last_error = LIBMONGODB_CAPI_SUCCESS; if (!libraryInitialized_) throw std::runtime_error("libmongodbcapi_init not called"); @@ -91,29 +86,7 @@ libmongodbcapi_db* db_new(int argc, const char** argv, const char** envp) noexce } global_db = new libmongodbcapi_db; - // iterate over argv and copy them to argvStorage - for (int i = 0; i < argc; i++) { - // allocate space for the null terminator - auto s = mongo::stdx::make_unique<char[]>(std::strlen(argv[i]) + 1); - // copy the string + null terminator - std::strncpy(s.get(), argv[i], std::strlen(argv[i]) + 1); - global_db->argvPointers.push_back(s.get()); - global_db->argvStorage.push_back(std::move(s)); - } - global_db->argvPointers.push_back(nullptr); - - // iterate over envp and copy them to envpStorage - while (envp != nullptr && *envp != nullptr) { - auto s = mongo::stdx::make_unique<char[]>(std::strlen(*envp) + 1); - std::strncpy(s.get(), *envp, std::strlen(*envp) + 1); - global_db->envpPointers.push_back(s.get()); - global_db->envpStorage.push_back(std::move(s)); - envp++; - } - global_db->envpPointers.push_back(nullptr); - - global_db->serviceContext = - embedded::initialize(argc, global_db->argvPointers.data(), global_db->envpPointers.data()); + global_db->serviceContext = embedded::initialize(yaml_config); if (!global_db->serviceContext) { delete global_db; global_db = nullptr; @@ -225,8 +198,8 @@ int libmongodbcapi_fini() { return LIBMONGODB_CAPI_SUCCESS; } -libmongodbcapi_db* libmongodbcapi_db_new(int argc, const char** argv, const char** envp) { - return mongo::db_new(argc, argv, envp); +libmongodbcapi_db* libmongodbcapi_db_new(const char* yaml_config) { + return mongo::db_new(yaml_config); } int libmongodbcapi_db_destroy(libmongodbcapi_db* db) { diff --git a/src/mongo/client/embedded/libmongodbcapi.h b/src/mongo/client/embedded/libmongodbcapi.h index 8c1646dd9b6..f20f6675a5d 100644 --- a/src/mongo/client/embedded/libmongodbcapi.h +++ b/src/mongo/client/embedded/libmongodbcapi.h @@ -79,16 +79,12 @@ int libmongodbcapi_fini(); /** * Starts the database and returns a handle with the service context. * -* @param argc -* The number of arguments in argv -* @param argv -* The arguments that will be passed to mongod at startup to initialize state -* @param envp -* Environment variables that will be passed to mongod at startup to initilize state +* @param config null-terminated YAML formatted MongoDB configuration. See documentation for valid +* options. * * @return A pointer to a db handle or null on error */ -libmongodbcapi_db* libmongodbcapi_db_new(int argc, const char** argv, const char** envp); +libmongodbcapi_db* libmongodbcapi_db_new(const char* yaml_config); /** * Shuts down the database diff --git a/src/mongo/client/embedded/libmongodbcapi_test.cpp b/src/mongo/client/embedded/libmongodbcapi_test.cpp index 8a388b2f757..74c6164d5bf 100644 --- a/src/mongo/client/embedded/libmongodbcapi_test.cpp +++ b/src/mongo/client/embedded/libmongodbcapi_test.cpp @@ -30,6 +30,7 @@ #include "mongo/client/embedded/libmongodbcapi.h" #include <set> +#include <yaml-cpp/yaml.h> #include "mongo/bson/bsonobjbuilder.h" #include "mongo/db/json.h" @@ -68,14 +69,19 @@ protected: if (!globalTempDir) { globalTempDir = mongo::stdx::make_unique<mongo::unittest::TempDir>("embedded_mongo"); } - const char* argv[] = {"mongo_embedded_capi_test", - "--port", - "0", - "--storageEngine", - "mobile", - "--dbpath", - globalTempDir->path().c_str()}; - db = libmongodbcapi_db_new(7, argv, nullptr); + + YAML::Emitter yaml; + yaml << YAML::BeginMap; + + yaml << YAML::Key << "storage"; + yaml << YAML::Value << YAML::BeginMap; + yaml << YAML::Key << "dbPath"; + yaml << YAML::Value << globalTempDir->path(); + yaml << YAML::EndMap; // storage + + yaml << YAML::EndMap; + + db = libmongodbcapi_db_new(yaml.c_str()); ASSERT(db != nullptr); } @@ -428,7 +434,7 @@ TEST_F(MongodbCAPITest, InsertAndUpdate) { // This test is temporary to make sure that only one database can be created // This restriction may be relaxed at a later time TEST_F(MongodbCAPITest, CreateMultipleDBs) { - libmongodbcapi_db* db2 = libmongodbcapi_db_new(0, nullptr, nullptr); + libmongodbcapi_db* db2 = libmongodbcapi_db_new(nullptr); ASSERT(db2 == nullptr); ASSERT_EQUALS(libmongodbcapi_get_last_error(), LIBMONGODB_CAPI_ERROR_UNKNOWN); } diff --git a/src/mongo/util/options_parser/options_parser.cpp b/src/mongo/util/options_parser/options_parser.cpp index 2137e7691d1..ec1705c4e03 100644 --- a/src/mongo/util/options_parser/options_parser.cpp +++ b/src/mongo/util/options_parser/options_parser.cpp @@ -1141,23 +1141,10 @@ Status OptionsParser::run(const OptionSection& options, return ret; } - YAML::Node YAMLConfig; - ret = parseYAMLConfigFile(config_file, &YAMLConfig); + ret = parseConfigFile(options, config_file, &configEnvironment); if (!ret.isOK()) { return ret; } - - if (isYAMLConfig(YAMLConfig)) { - ret = addYAMLNodesToEnvironment(YAMLConfig, options, "", &configEnvironment); - if (!ret.isOK()) { - return ret; - } - } else { - ret = parseINIConfigFile(options, config_file, &configEnvironment); - if (!ret.isOK()) { - return ret; - } - } } // Adds the values for all our options that were registered as composable to the composed @@ -1211,5 +1198,56 @@ Status OptionsParser::run(const OptionSection& options, return Status::OK(); } +Status OptionsParser::runConfigFile( + const OptionSection& options, + const std::string& config, + const std::map<std::string, std::string>& env, // Unused, interface consistent with run() + Environment* configEnvironment) { + // Add the default values to our resulting environment + Status ret = addDefaultValues(options, configEnvironment); + if (!ret.isOK()) { + return ret; + } + + // Add values from the provided config file + ret = parseConfigFile(options, config, configEnvironment); + if (!ret.isOK()) { + return ret; + } + + // Add the constraints from our options to the result environment + ret = addConstraints(options, configEnvironment); + if (!ret.isOK()) { + return ret; + } + + return ret; +} + +Status OptionsParser::parseConfigFile(const OptionSection& options, + const std::string& config_file, + Environment* configEnvironment) { + YAML::Node YAMLConfig; + Status ret = parseYAMLConfigFile(config_file, &YAMLConfig); + if (!ret.isOK()) { + return ret; + } + + // Check if YAML parsing was successful, if not try to read as INI + if (isYAMLConfig(YAMLConfig)) { + ret = addYAMLNodesToEnvironment(YAMLConfig, options, "", configEnvironment); + if (!ret.isOK()) { + return ret; + } + } else { + ret = parseINIConfigFile(options, config_file, configEnvironment); + if (!ret.isOK()) { + return ret; + } + } + + return ret; +} + } // namespace optionenvironment } // namespace mongo diff --git a/src/mongo/util/options_parser/options_parser.h b/src/mongo/util/options_parser/options_parser.h index 26954debb59..a5beab53d97 100644 --- a/src/mongo/util/options_parser/options_parser.h +++ b/src/mongo/util/options_parser/options_parser.h @@ -110,6 +110,15 @@ public: const std::map<std::string, std::string>& env, Environment*); + /** Handles parsing of a YAML or INI formatted string. The + * OptionSection be a description of the allowed options. This function populates the + * given Environment with the results but does not call validate on the Environment. + */ + Status runConfigFile(const OptionSection&, + const std::string& config, + const std::map<std::string, std::string>& env, + Environment*); + private: /** Handles parsing of the command line and adds the results to the given Environment */ Status parseCommandLine(const OptionSection&, @@ -120,6 +129,10 @@ private: * */ Status parseINIConfigFile(const OptionSection&, const std::string& config, Environment*); + /** Handles parsing of either YAML or INI config and adds the results to the given Environment + */ + Status parseConfigFile(const OptionSection&, const std::string& argv, Environment*); + /** Gets defaults from the OptionSection and adds them to the given Environment */ Status addDefaultValues(const OptionSection&, Environment*); |