22 #include "mongodb_instance_config.h"
26 #include <config/config.h>
27 #include <core/exceptions/system.h>
28 #include <utils/sub_process/proc.h>
29 #include <utils/time/wait.h>
31 #include <boost/filesystem.hpp>
32 #include <bsoncxx/builder/basic/document.hpp>
33 #include <bsoncxx/json.hpp>
35 #include <mongocxx/client.hpp>
36 #include <mongocxx/exception/exception.hpp>
42 using namespace std::chrono_literals;
62 set_name(
"MongoDBInstance|%s", cfgname.c_str());
63 config_name_ = cfgname;
69 enabled_ = config->
get_bool(prefix +
"enabled");
74 startup_grace_period_ = 10;
76 startup_grace_period_ = config->
get_uint(prefix +
"startup-grace-period");
81 loop_interval_ = config->
get_float(prefix +
"loop-interval");
84 termination_grace_period_ = config->
get_uint(prefix +
"termination-grace-period");
85 clear_data_on_termination_ = config->
get_bool(prefix +
"clear-data-on-termination");
86 port_ = config->
get_uint(prefix +
"port");
88 data_path_ = config->
get_string(prefix +
"data-path");
89 log_path_ = config->
get_string(prefix +
"log/path");
90 log_append_ = config->
get_bool(prefix +
"log/append");
92 replica_set_ = config->
get_string(prefix +
"replica-set");
96 if (!replica_set_.empty()) {
99 oplog_size_ = config->
get_uint(prefix +
"oplog-size");
106 "mongod",
"--bind_ip", bind_ip_,
"--port", std::to_string(port_),
"--dbpath", data_path_};
108 if (!log_path_.empty()) {
110 argv_.push_back(
"--logappend");
112 argv_.push_back(
"--logpath");
113 argv_.push_back(log_path_);
116 if (!replica_set_.empty()) {
117 argv_.push_back(
"--replSet");
118 argv_.push_back(replica_set_);
119 if (oplog_size_ > 0) {
120 argv_.push_back(
"--oplogSize");
121 argv_.push_back(std::to_string(oplog_size_));
127 if (!extra_args.empty()) {
129 int wrv = wordexp(extra_args.c_str(), &p, WRDE_NOCMD | WRDE_UNDEF);
132 case WRDE_BADCHAR:
throw Exception(
"%s: invalid character in args",
name());
133 case WRDE_BADVAL:
throw Exception(
"%s: undefined variable referenced in args",
name());
135 throw Exception(
"%s: running sub-commands has been disabled for args",
name());
137 case WRDE_SYNTAX:
throw Exception(
"%s: shell syntax error in args",
name());
138 default:
throw Exception(
"Unexpected wordexp error %d when parsing args", wrv);
143 std::vector<std::string> invalid_args = {
"--port",
156 for (
size_t i = 0; i < p.we_wordc; ++i) {
157 for (
size_t j = 0; j < invalid_args.size(); ++j) {
158 if (invalid_args[j] == p.we_wordv[i]) {
160 throw Exception(
"%s: %s may not be passed in args",
name(), invalid_args[j].c_str());
163 argv_.push_back(p.we_wordv[i]);
169 command_line_ = std::accumulate(std::next(argv_.begin()),
172 [](std::string &s,
const std::string &a) { return s +
" " + a; });
183 "clear data on termination: %s",
184 clear_data_on_termination_ ?
"yes" :
"no");
190 replica_set_.empty() ?
"DISABLED" : replica_set_.c_str());
191 if (!replica_set_.empty()) {
197 throw Exception(
"Instance '%s' cannot be started while disabled",
name());
200 timewait_ =
new TimeWait(
clock, (
int)(loop_interval_ * 1000000.));
207 if (!running_ || !check_alive()) {
233 return command_line_;
242 return termination_grace_period_;
246 MongoDBInstanceConfig::check_alive()
249 mongocxx::client client{mongocxx::uri(
"mongodb://localhost:" + std::to_string(port_))};
251 using namespace bsoncxx::builder;
252 auto cmd{basic::document{}};
253 cmd.append(basic::kvp(
"isMaster", 1));
255 auto reply = client[
"admin"].run_command(cmd.view());
256 bool ok = check_mongodb_ok(reply.view());
258 logger->
log_warn(
name(),
"Failed to connect: %s", bsoncxx::to_json(reply.view()).c_str());
261 }
catch (mongocxx::exception &e) {
281 boost::filesystem::create_directories(data_path_);
282 }
catch (boost::filesystem::filesystem_error &e) {
283 throw Exception(
"Failed to create data path '%s' for mongod(%s): %s",
285 config_name_.c_str(),
289 if (!log_path_.empty()) {
290 boost::filesystem::path p(log_path_);
292 boost::filesystem::create_directories(p.parent_path());
293 }
catch (boost::filesystem::filesystem_error &e) {
294 throw Exception(
"Failed to create log path '%s' for mongod(%s): %s",
295 p.parent_path().string().c_str(),
296 config_name_.c_str(),
301 std::string progname =
"mongod(" + config_name_ +
")";
303 std::make_shared<SubProcess>(progname,
"mongod", argv_, std::vector<std::string>{},
logger);
305 for (
unsigned i = 0; i < startup_grace_period_ * 4; ++i) {
310 std::this_thread::sleep_for(250ms);
314 throw Exception(
"%s: instance did not start in time",
name());
328 for (
unsigned i = 0; i < termination_grace_period_; ++i) {
331 std::this_thread::sleep_for(1s);
338 boost::filesystem::remove_all(data_path_);
339 }
catch (boost::filesystem::filesystem_error &e) {
340 throw Exception(
"Failed to create data path '%s' for mongod(%s): %s",
342 config_name_.c_str(),