Fawkes API  Fawkes Development Version
mongodb_client_config.cpp
1 
2 /***************************************************************************
3  * mongodb_client_config.cpp - MongoDB client configuration
4  *
5  * Created: Wed Jul 12 13:45:03 2017
6  * Copyright 2006-2017 Tim Niemueller [www.niemueller.de]
7  ****************************************************************************/
8 
9 /* This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Library General Public License for more details.
18  *
19  * Read the full text in the LICENSE.GPL file in the doc directory.
20  */
21 
22 #include "mongodb_client_config.h"
23 
24 #include <config/config.h>
25 #include <logging/logger.h>
26 
27 #include <chrono>
28 #include <thread>
29 
30 using namespace fawkes;
31 
32 /** @class MongoDBClientConfig "mongodb_client_config.h"
33  * MongoDB Client Configuration.
34  * Instances of this class represent a single MongoDB client configuration
35  * used to initiate connections.
36  *
37  * @author Tim Niemueller
38  */
39 
40 /** Constructor.
41  * This will read the given configuration.
42  * @param config configuration to query
43  * @param logger logger for info messages
44  * @param cfgname configuration name
45  * @param prefix configuration path prefix
46  */
48  Logger * logger,
49  std::string cfgname,
50  std::string prefix)
51 {
52  logcomp_ = "MongoDBClient|" + cfgname;
53 
54  enabled_ = false;
55  try {
56  enabled_ = config->get_bool((prefix + "enabled").c_str());
57  } catch (Exception &e) {
58  }
59 
60  std::string mode = "connection";
61  try {
62  mode = config->get_string((prefix + "mode").c_str());
63  } catch (Exception &e) {
64  logger->log_info(logcomp_.c_str(),
65  "MongoDB config '%s' specifies no client "
66  "mode, assuming 'connection'.",
67  cfgname.c_str());
68  }
69 
70  startup_grace_period_ = 10;
71  try {
72  startup_grace_period_ = config->get_uint(prefix + "startup_grace_period");
73  } catch (Exception &e) {
74  logger->log_info(logcomp_.c_str(),
75  "MongoDB config '%s' specifies no startup grace period "
76  ", assuming 10 seconds.",
77  cfgname.c_str());
78  }
79 
80  auth_string_ = "";
81  read_authinfo(config, logger, cfgname, prefix);
82 
83  if (mode == "replica_set" || mode == "replicaset") {
84  mode_ = REPLICA_SET;
85  replicaset_name_ = config->get_string((prefix + "name").c_str());
86 
87  std::vector<std::string> hosts = config->get_strings(prefix + "hosts");
88  std::string uri{"mongodb://"};
89  for (auto it = hosts.begin(); it != hosts.end(); it++) {
90  uri += *it;
91  if (std::next(it) != hosts.end()) {
92  uri += ",";
93  }
94  }
95  uri += "/" + auth_dbname;
96  uri += "?replicaSet=" + replicaset_name_;
97  try {
98  uri += "&readPreference=" + config->get_string((prefix + "read-preference").c_str());
99  } catch (Exception &e) {
100  // use default read preference
101  }
102  try {
103  uri += "&readPreferenceTags=" + config->get_string((prefix + "read-preference-tags").c_str());
104  } catch (Exception &e) {
105  }
106  conn_uri_ = mongocxx::uri{uri};
107 
108  } else if (mode == "sync_cluster" || mode == "synccluster") {
109  throw Exception("sync_cluster connections are no longer supported");
110 
111  } else {
112  mode_ = CONNECTION;
113  conn_uri_ = mongocxx::uri{"mongodb://" + auth_string_ + config->get_string(prefix + "hostport")
114  + "/" + auth_dbname};
115  }
116 }
117 
118 /** Read authentication info for given configuration.
119  * This will try to read the fields auth_dbname, auth_username, and
120  * auth_password.
121  * @param config configuration to query
122  * @param logger logger for info messages
123  * @param cfgname configuration name
124  * @param prefix configuration path prefix
125  */
126 void
127 MongoDBClientConfig::read_authinfo(Configuration *config,
128  Logger * logger,
129  std::string cfgname,
130  std::string prefix)
131 {
132  try {
133  auth_dbname = config->get_string((prefix + "auth_dbname").c_str());
134  std::string username = config->get_string((prefix + "auth_username").c_str());
135  std::string password = config->get_string((prefix + "auth_password").c_str());
136  if (!username.empty() && !password.empty()) {
137  auth_string_ = username + ":" + password + "@";
138  }
139  } catch (Exception &e) {
140  logger->log_info(logcomp_.c_str(),
141  "No default authentication info for "
142  "MongoDB client '%s'",
143  cfgname.c_str());
144  }
145 }
146 
147 /** Create MongoDB client for this configuration.
148  * @return MongoDB client
149  */
150 mongocxx::client *
152 {
153  for (unsigned int startup_tries = 0; startup_tries < startup_grace_period_ * 2; ++startup_tries) {
154  try {
155  return new mongocxx::client(conn_uri_);
156  } catch (fawkes::Exception &) {
157  using namespace std::chrono_literals;
158  std::this_thread::sleep_for(500ms);
159  }
160  }
161  throw Exception("Failed to create client with uri: '%s'", conn_uri_.to_string().c_str());
162 }
163 
164 /** Write client configuration information to log.
165  * @param logger logger to write to
166  * @param component component to pass to logger
167  * @param indent indentation to put before each string
168  */
169 void
170 MongoDBClientConfig::log(Logger *logger, const char *component, const char *indent)
171 {
172  switch (mode_) {
173  case REPLICA_SET: {
174  logger->log_info(component, "%smode: replica set", indent);
175  } break;
176  default: {
177  logger->log_info(component, "%smode: connection", indent);
178  } break;
179  }
180  std::string uri_string = conn_uri_.to_string();
181  if (!auth_string_.empty()) {
182  if (uri_string.find(auth_string_) != std::string::npos) {
183  uri_string.replace(uri_string.find(auth_string_), auth_string_.length(), "****@****");
184  }
185  }
186  logger->log_info(component, "%suri: %s", indent, uri_string.c_str());
187 }
188 
189 /** Get host and port of configuration.
190  * @return string of the form "host:port"
191  */
192 std::string
194 {
195  auto hosts = conn_uri_.hosts();
196  if (hosts.empty()) {
197  return "";
198  } else {
199  // If multiple hosts are specified, only use the first host.
200  return hosts[0].name + ":" + std::to_string(hosts[0].port);
201  }
202 }
203 
204 /** Get client configuration mode.
205  * @return mode, connection or replica set
206  */
209 {
210  return mode_;
211 }
mongocxx::client * create_client()
Create MongoDB client for this configuration.
ConnectionMode
Connection mode enumeration.
MongoDBClientConfig(fawkes::Configuration *config, fawkes::Logger *logger, std::string cfgname, std::string prefix)
Constructor.
void log(fawkes::Logger *logger, const char *component, const char *indent)
Write client configuration information to log.
std::string hostport() const
Get host and port of configuration.
ConnectionMode mode() const
Get client configuration mode.
Interface for configuration handling.
Definition: config.h:65
virtual unsigned int get_uint(const char *path)=0
Get value from configuration which is of type unsigned int.
virtual bool get_bool(const char *path)=0
Get value from configuration which is of type bool.
virtual std::vector< std::string > get_strings(const char *path)=0
Get list of values from configuration which is of type string.
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
Base class for exceptions in Fawkes.
Definition: exception.h:36
Interface for logging.
Definition: logger.h:42
virtual void log_info(const char *component, const char *format,...)
Log informational message.
Definition: multi.cpp:195
Fawkes library namespace.