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
30using 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 */
126void
127MongoDBClientConfig::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 */
150mongocxx::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 */
169void
170MongoDBClientConfig::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 */
192std::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.
@ CONNECTION
connect to single node
@ REPLICA_SET
connect to replica set
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:68
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.