diff options
author | Anton Popovichenko <walkline.ua@gmail.com> | 2021-08-19 22:26:16 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2022-03-13 16:37:01 +0100 |
commit | 8447f627bac611b7f5450a024f7f5febc9481190 (patch) | |
tree | 173d1610ca62084be8494b3b142718df16e50f16 /src/common | |
parent | 1e9327d92c31fa1af4371b8f32c0548b029a82f6 (diff) |
Core/Config: Implement config override with env vars (#26811)
* Core/Config: Implement config override with env vars
Implement overriding of configuration from the .conf file with environment variables.
Environment variables keys are autogenerated based on the keys defined in .conf file.
Usage example:
$ export TC_DATA_DIR=/usr
$ TC_WORLD_SERVER_PORT=8080 ./worldserver
* Core/Config Fix typo in logs
Co-authored-by: Giacomo Pozzoni <giacomopoz@gmail.com>
* Core/Config Fix code style in EnvVarForIniKey
Co-authored-by: Shauren <shauren.trinity@gmail.com>
* Update tests/common/Config.cpp
* Apply suggestions from code review
Co-authored-by: Peter Keresztes Schmidt <carbenium@outlook.com>
* Apply suggestions from code review
Co-authored-by: Peter Keresztes Schmidt <carbenium@outlook.com>
Co-authored-by: Anton Popovichenko <anton.popovichenko@mendix.com>
Co-authored-by: Giacomo Pozzoni <giacomopoz@gmail.com>
Co-authored-by: Shauren <shauren.trinity@gmail.com>
Co-authored-by: Peter Keresztes Schmidt <carbenium@outlook.com>
(cherry picked from commit 1ddd9dc19cc1df1a1ab8c6123283999f9dea6760)
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/Configuration/Config.cpp | 128 | ||||
-rw-r--r-- | src/common/Configuration/Config.h | 3 |
2 files changed, 129 insertions, 2 deletions
diff --git a/src/common/Configuration/Config.cpp b/src/common/Configuration/Config.cpp index be9ad42031c..9a02627d2b7 100644 --- a/src/common/Configuration/Config.cpp +++ b/src/common/Configuration/Config.cpp @@ -20,6 +20,7 @@ #include "StringConvert.h" #include <boost/property_tree/ini_parser.hpp> #include <algorithm> +#include <cstdlib> #include <memory> #include <mutex> @@ -56,6 +57,81 @@ namespace return true; } + + // Converts ini keys to the environment variable key (upper snake case). + // Example of conversions: + // SomeConfig => SOME_CONFIG + // myNestedConfig.opt1 => MY_NESTED_CONFIG_OPT_1 + // LogDB.Opt.ClearTime => LOG_DB_OPT_CLEAR_TIME + std::string IniKeyToEnvVarKey(std::string const& key) + { + std::string result; + + const char *str = key.c_str(); + size_t n = key.length(); + + char curr; + bool isEnd; + bool nextIsUpper; + bool currIsNumeric; + bool nextIsNumeric; + + for (size_t i = 0; i < n; ++i) + { + curr = str[i]; + if (curr == ' ' || curr == '.' || curr == '-') + { + result += '_'; + continue; + } + + isEnd = i == n - 1; + if (!isEnd) + { + nextIsUpper = isupper(str[i + 1]); + + // handle "aB" to "A_B" + if (!isupper(curr) && nextIsUpper) + { + result += static_cast<char>(std::toupper(curr)); + result += '_'; + continue; + } + + currIsNumeric = isNumeric(curr); + nextIsNumeric = isNumeric(str[i + 1]); + + // handle "a1" to "a_1" + if (!currIsNumeric && nextIsNumeric) + { + result += static_cast<char>(std::toupper(curr)); + result += '_'; + continue; + } + + // handle "1a" to "1_a" + if (currIsNumeric && !nextIsNumeric) + { + result += static_cast<char>(std::toupper(curr)); + result += '_'; + continue; + } + } + + result += static_cast<char>(std::toupper(curr)); + } + return result; + } + + Optional<std::string> EnvVarForIniKey(std::string const& key) + { + std::string envKey = "TC_" + IniKeyToEnvVarKey(key); + char* val = std::getenv(envKey.c_str()); + if (!val) + return std::nullopt; + + return std::string(val); + } } bool ConfigMgr::LoadInitial(std::string file, std::vector<std::string> args, @@ -91,6 +167,29 @@ bool ConfigMgr::LoadAdditionalFile(std::string file, bool keepOnReload, std::str return true; } +std::vector<std::string> ConfigMgr::OverrideWithEnvVariablesIfAny() +{ + std::lock_guard<std::mutex> lock(_configLock); + + std::vector<std::string> overriddenKeys; + + for (bpt::ptree::value_type& itr: _config) + { + if (!itr.second.empty() || itr.first.empty()) + continue; + + Optional<std::string> envVar = EnvVarForIniKey(itr.first); + if (!envVar) + continue; + + itr.second = bpt::ptree(*envVar); + + overriddenKeys.push_back(itr.first); + } + + return overriddenKeys; +} + ConfigMgr* ConfigMgr::instance() { static ConfigMgr instance; @@ -107,6 +206,8 @@ bool ConfigMgr::Reload(std::vector<std::string>& errors) if (!LoadAdditionalFile(additionalFile, false, error)) errors.push_back(std::move(error)); + OverrideWithEnvVariablesIfAny(); + return errors.empty(); } @@ -119,7 +220,22 @@ T ConfigMgr::GetValueDefault(std::string const& name, T def, bool quiet) const } catch (bpt::ptree_bad_path const&) { - if (!quiet) + Optional<std::string> envVar = EnvVarForIniKey(name); + if (envVar) + { + Optional<T> castedVar = Trinity::StringTo<T>(*envVar); + if (!castedVar) + { + TC_LOG_ERROR("server.loading", "Bad value defined for name %s in environment variables, going to use default instead", name.c_str()); + return def; + } + + if (!quiet) + TC_LOG_WARN("server.loading", "Missing name %s in config file %s, recovered with environment '%s' value.", name.c_str(), _filename.c_str(), envVar->c_str()); + + return *castedVar; + } + else if (!quiet) { TC_LOG_WARN("server.loading", "Missing name %s in config file %s, add \"%s = %s\" to this file", name.c_str(), _filename.c_str(), name.c_str(), std::to_string(def).c_str()); @@ -143,7 +259,15 @@ std::string ConfigMgr::GetValueDefault<std::string>(std::string const& name, std } catch (bpt::ptree_bad_path const&) { - if (!quiet) + Optional<std::string> envVar = EnvVarForIniKey(name); + if (envVar) + { + if (!quiet) + TC_LOG_WARN("server.loading", "Missing name %s in config file %s, recovered with environment '%s' value.", name.c_str(), _filename.c_str(), envVar->c_str()); + + return *envVar; + } + else if (!quiet) { TC_LOG_WARN("server.loading", "Missing name %s in config file %s, add \"%s = %s\" to this file", name.c_str(), _filename.c_str(), name.c_str(), def.c_str()); diff --git a/src/common/Configuration/Config.h b/src/common/Configuration/Config.h index 6a37be56bd6..3a67563c1db 100644 --- a/src/common/Configuration/Config.h +++ b/src/common/Configuration/Config.h @@ -34,6 +34,9 @@ public: bool LoadInitial(std::string file, std::vector<std::string> args, std::string& error); bool LoadAdditionalFile(std::string file, bool keepOnReload, std::string& error); + /// Overrides configuration with environment variables and returns overridden keys + std::vector<std::string> OverrideWithEnvVariablesIfAny(); + static ConfigMgr* instance(); bool Reload(std::vector<std::string>& errors); |