2 Copyright (c) 2018-2019 Nokia.
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
17 #include "private/abort.hpp"
18 #include "private/configurationreader.hpp"
19 #include <boost/property_tree/json_parser.hpp>
20 #include <sdl/exception.hpp>
21 #include "private/createlogger.hpp"
22 #include "private/databaseconfiguration.hpp"
23 #include "private/logger.hpp"
24 #include "private/namespaceconfigurations.hpp"
25 #include "private/namespacevalidator.hpp"
26 #include "private/system.hpp"
28 using namespace shareddatalayer;
33 T get(const boost::property_tree::ptree& ptree, const std::string& param, const std::string& sourceName)
37 return ptree.get<T>(param);
39 catch (const boost::property_tree::ptree_bad_path&)
41 std::ostringstream os;
42 os << "Configuration error in " << sourceName << ": "
43 << "missing \"" << param << '\"';
44 throw Exception(os.str());
46 catch (const boost::property_tree::ptree_bad_data& e)
48 std::ostringstream os;
49 os << "Configuration error in " << sourceName << ": "
50 << "invalid \"" << param << "\": \"" << e.data<boost::property_tree::ptree::data_type>() << '\"';
51 throw Exception(os.str());
55 void validateAndSetDbType(const std::string& type, DatabaseConfiguration& databaseConfiguration,
56 const std::string& sourceName)
60 databaseConfiguration.checkAndApplyDbType(type);
62 catch (const std::exception& e)
64 std::ostringstream os;
65 os << "Configuration error in " << sourceName << ": "
67 throw Exception(os.str());
71 void validateAndSetDbServerAddress(const std::string& address, DatabaseConfiguration& databaseConfiguration,
72 const std::string& sourceName)
76 databaseConfiguration.checkAndApplyServerAddress(address);
78 catch (const std::exception& e)
80 std::ostringstream os;
81 os << "Configuration error in " << sourceName << ": "
82 << "invalid \"address\": \"" << address << "\" " << e.what();
83 throw Exception(os.str());
87 void parseDatabaseServerConfiguration(DatabaseConfiguration& databaseConfiguration,
88 const boost::property_tree::ptree& ptree,
89 const std::string& sourceName)
91 const auto address(get<std::string>(ptree, "address", sourceName));
92 validateAndSetDbServerAddress(address, databaseConfiguration, sourceName);
95 void parseDatabaseServersConfiguration(DatabaseConfiguration& databaseConfiguration,
96 const boost::property_tree::ptree& ptree,
97 const std::string& sourceName)
99 const auto servers(ptree.get_child_optional("servers"));
101 for(const auto& server : *servers)
102 parseDatabaseServerConfiguration(databaseConfiguration, server.second, sourceName);
105 std::ostringstream os;
106 os << "Configuration error in " << sourceName << ": "
107 << "missing \"servers\"";
108 throw Exception(os.str());
112 void parseDatabaseConfiguration(DatabaseConfiguration& databaseConfiguration,
113 const boost::property_tree::ptree& ptree,
114 const std::string& sourceName)
116 const auto type(get<std::string>(ptree, "type", sourceName));
117 validateAndSetDbType(type, databaseConfiguration, sourceName);
119 parseDatabaseServersConfiguration(databaseConfiguration, ptree, sourceName);
122 void parseDatabaseConfigurationTree(DatabaseConfiguration& databaseConfiguration,
123 const boost::optional<boost::property_tree::ptree>& databaseConfigurationPtree,
124 const std::string& sourceName)
126 if (databaseConfigurationPtree)
127 parseDatabaseConfiguration(databaseConfiguration, *databaseConfigurationPtree, sourceName);
130 void parseDatabaseServersConfigurationFromString(DatabaseConfiguration& databaseConfiguration,
131 const std::string& serverConfiguration,
132 const std::string& sourceName)
138 auto split = serverConfiguration.find(',', base);
139 done = std::string::npos == split;
140 validateAndSetDbServerAddress(serverConfiguration.substr(base, done ? std::string::npos : split-base),
141 databaseConfiguration,
147 void validateNamespacePrefix(const std::string& prefix,
148 const std::string& sourceName)
150 if (!isValidNamespaceSyntax(prefix))
152 std::ostringstream os;
153 os << "Configuration error in " << sourceName << ": "
154 << "\"namespacePrefix\": \"" << prefix << "\""
155 << " contains some of these disallowed characters: "
156 << getDisallowedCharactersInNamespace();
157 throw Exception(os.str());
161 void validateEnableNotifications(bool enableNotifications, bool useDbBackend,
162 const std::string& sourceName)
164 if (enableNotifications && !useDbBackend)
166 std::ostringstream os;
167 os << "Configuration error in " << sourceName << ": "
168 << "\"enableNotifications\" cannot be true, when \"useDbBackend\" is false";
169 throw Exception(os.str());
173 void parseNsConfiguration(NamespaceConfigurations& namespaceConfigurations,
174 const std::string& namespacePrefix,
175 const boost::property_tree::ptree& ptree,
176 const std::string& sourceName)
178 const auto useDbBackend(get<bool>(ptree, "useDbBackend", sourceName));
179 const auto enableNotifications(get<bool>(ptree, "enableNotifications", sourceName));
181 validateNamespacePrefix(namespacePrefix, sourceName);
182 validateEnableNotifications(enableNotifications, useDbBackend, sourceName);
184 namespaceConfigurations.addNamespaceConfiguration({namespacePrefix, useDbBackend, enableNotifications, sourceName});
187 void parseNsConfigurationMap(NamespaceConfigurations& namespaceConfigurations,
188 std::unordered_map<std::string, std::pair<boost::property_tree::ptree, std::string>>& namespaceConfigurationMap)
190 for (const auto &namespaceConfigurationMapItem : namespaceConfigurationMap )
191 parseNsConfiguration(namespaceConfigurations, namespaceConfigurationMapItem.first, namespaceConfigurationMapItem.second.first, namespaceConfigurationMapItem.second.second);
195 ConfigurationReader::ConfigurationReader(std::shared_ptr<Logger> logger):
196 ConfigurationReader(getDefaultConfDirectories(), System::getSystem(), logger)
200 ConfigurationReader::ConfigurationReader(const Directories& directories,
202 std::shared_ptr<Logger> logger):
203 dbHostEnvVariableName(DB_HOST_ENV_VAR_NAME),
204 dbHostEnvVariableValue({}),
205 dbPortEnvVariableName(DB_PORT_ENV_VAR_NAME),
206 dbPortEnvVariableValue({}),
207 sentinelPortEnvVariableName(SENTINEL_PORT_ENV_VAR_NAME),
208 sentinelPortEnvVariableValue({}),
209 sentinelMasterNameEnvVariableName(SENTINEL_MASTER_NAME_ENV_VAR_NAME),
210 sentinelMasterNameEnvVariableValue({}),
211 jsonDatabaseConfiguration(boost::none),
214 auto envStr = system.getenv(dbHostEnvVariableName.c_str());
217 dbHostEnvVariableValue = envStr;
218 sourceForDatabaseConfiguration = dbHostEnvVariableName;
219 auto envStr = system.getenv(dbPortEnvVariableName.c_str());
221 dbPortEnvVariableValue = envStr;
222 envStr = system.getenv(sentinelPortEnvVariableName.c_str());
224 sentinelPortEnvVariableValue = envStr;
225 envStr = system.getenv(sentinelMasterNameEnvVariableName.c_str());
227 sentinelMasterNameEnvVariableValue = envStr;
230 readConfigurationFromDirectories(directories);
233 ConfigurationReader::~ConfigurationReader()
237 void ConfigurationReader::readConfigurationFromDirectories(const Directories& directories)
239 for (const auto& i : findConfigurationFiles(directories))
240 readConfiguration(i, i);
243 void ConfigurationReader::readConfigurationFromInputStream(const std::istream& input)
245 jsonNamespaceConfigurations.clear();
246 readConfiguration(const_cast<std::istream&>(input), "<istream>");
250 void ConfigurationReader::readConfiguration(T& input, const std::string& currentSourceName)
252 boost::property_tree::ptree propertyTree;
256 boost::property_tree::read_json(input, propertyTree);
258 catch (const boost::property_tree::json_parser::json_parser_error& e)
260 std::ostringstream os;
261 os << "error in SDL configuration " << currentSourceName << " at line " << e.line() << ": ";
263 logger->error() << os.str();
264 throw Exception(os.str());
267 // Environment variable configuration overrides json configuration
268 if (sourceForDatabaseConfiguration != dbHostEnvVariableName)
270 const auto databaseConfiguration(propertyTree.get_child_optional("database"));
271 if (databaseConfiguration)
273 jsonDatabaseConfiguration = databaseConfiguration;
274 sourceForDatabaseConfiguration = currentSourceName;
278 const auto namespaceConfigurations(propertyTree.get_child_optional("sharedDataLayer"));
279 if (namespaceConfigurations)
281 for(const auto& namespaceConfiguration : *namespaceConfigurations)
283 const auto namespacePrefix = get<std::string>(namespaceConfiguration.second, "namespacePrefix", currentSourceName);
284 jsonNamespaceConfigurations[namespacePrefix] = std::make_pair(namespaceConfiguration.second, currentSourceName);
289 void ConfigurationReader::readDatabaseConfiguration(DatabaseConfiguration& databaseConfiguration)
291 if (!databaseConfiguration.isEmpty())
292 SHAREDDATALAYER_ABORT("Database configuration can be read only to empty container");
296 if (sourceForDatabaseConfiguration == dbHostEnvVariableName)
298 // NOTE: Redis cluster is not currently configurable via environment variables.
299 if (sentinelPortEnvVariableValue.empty())
301 validateAndSetDbType("redis-standalone", databaseConfiguration, sourceForDatabaseConfiguration);
302 if (dbPortEnvVariableValue.empty())
303 parseDatabaseServersConfigurationFromString(databaseConfiguration, dbHostEnvVariableValue, sourceForDatabaseConfiguration);
305 parseDatabaseServersConfigurationFromString(databaseConfiguration, dbHostEnvVariableValue + ":" + dbPortEnvVariableValue, sourceForDatabaseConfiguration);
309 validateAndSetDbType("redis-sentinel", databaseConfiguration, sourceForDatabaseConfiguration);
310 databaseConfiguration.checkAndApplySentinelAddress(dbHostEnvVariableValue + ":" + sentinelPortEnvVariableValue);
311 databaseConfiguration.checkAndApplySentinelMasterName(sentinelMasterNameEnvVariableValue);
315 parseDatabaseConfigurationTree(databaseConfiguration, jsonDatabaseConfiguration, sourceForDatabaseConfiguration);
317 catch (const std::exception& e)
319 logger->error() << e.what();
324 void ConfigurationReader::readNamespaceConfigurations(NamespaceConfigurations& namespaceConfigurations)
326 if (!namespaceConfigurations.isEmpty())
327 SHAREDDATALAYER_ABORT("Namespace configurations can be read only to empty container");
331 parseNsConfigurationMap(namespaceConfigurations, jsonNamespaceConfigurations);
333 catch(const std::exception& e)
335 logger->error() << e.what();
341 template void ConfigurationReader::readConfiguration(const std::string&, const std::string&);
342 template void ConfigurationReader::readConfiguration(std::istream&, const std::string&);