aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/config.cpp
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2023-08-31 17:23:39 -0400
committerGitHub <[email protected]>2023-08-31 23:23:39 +0200
commit9fb07c67657d5f4e6993c03fe18ba04ad0c2d416 (patch)
tree94b21805e724cb15c811ceb3bcbb9ce8fe0e3292 /src/zenserver/config.cpp
parentproject store gc deadlock (#381) (diff)
downloadzen-9fb07c67657d5f4e6993c03fe18ba04ad0c2d416.tar.xz
zen-9fb07c67657d5f4e6993c03fe18ba04ad0c2d416.zip
lua config file improvements (#379)
- Bugfix: All options given on command line now overrides lua config file settings - Improvement: All options available from command line can now be configured in the lua config file (with a few exceptions such as `owner-pid`, `install` and `uninstall`) We can now also generate a Lua config (just string so far) that includes all options given on command line together with any optional Lua input config file. The idea here is to be able to save config options in the future.
Diffstat (limited to 'src/zenserver/config.cpp')
-rw-r--r--src/zenserver/config.cpp1091
1 files changed, 791 insertions, 300 deletions
diff --git a/src/zenserver/config.cpp b/src/zenserver/config.cpp
index 0e7aac079..2d286b02a 100644
--- a/src/zenserver/config.cpp
+++ b/src/zenserver/config.cpp
@@ -135,7 +135,795 @@ ParseBucketConfigs(std::span<std::string> Buckets)
return Cfg;
}
-void ParseConfigFile(const std::filesystem::path& Path, ZenServerOptions& ServerOptions);
+static std::string
+MakeSafePath(const std::string& Path)
+{
+#if ZEN_PLATFORM_WINDOWS
+ if (Path.empty())
+ {
+ return Path;
+ }
+
+ std::string FixedPath = Path;
+ std::replace(FixedPath.begin(), FixedPath.end(), '/', '\\');
+ if (!FixedPath.starts_with("\\\\?\\"))
+ {
+ FixedPath.insert(0, "\\\\?\\");
+ }
+ return FixedPath;
+#else
+ return Path;
+#endif
+};
+
+namespace LuaConfig {
+
+void
+EscapeBackslash(std::string& InOutString)
+{
+ std::size_t BackslashPos = InOutString.find('\\');
+ if (BackslashPos != std::string::npos)
+ {
+ std::size_t Offset = 0;
+ zen::ExtendableStringBuilder<512> PathBuilder;
+ while (BackslashPos != std::string::npos)
+ {
+ PathBuilder.Append(InOutString.substr(Offset, BackslashPos + 1 - Offset));
+ PathBuilder.Append('\\');
+ Offset = BackslashPos + 1;
+ BackslashPos = InOutString.find('\\', Offset);
+ }
+ PathBuilder.Append(InOutString.substr(Offset, BackslashPos));
+ InOutString = PathBuilder.ToString();
+ }
+}
+
+class OptionValue
+{
+public:
+ virtual void Print(std::string_view Indent, zen::StringBuilderBase& StringBuilder) = 0;
+ virtual void Parse(sol::object Object) = 0;
+
+ virtual ~OptionValue() {}
+};
+
+typedef std::shared_ptr<OptionValue> TOptionValue;
+
+class StringOption : public OptionValue
+{
+public:
+ StringOption(std::string& Value) : Value(Value) {}
+ virtual void Print(std::string_view, zen::StringBuilderBase& StringBuilder) override
+ {
+ StringBuilder.Append(fmt::format("\"{}\"", Value));
+ }
+ virtual void Parse(sol::object Object) override { Value = Object.as<std::string>(); }
+ std::string& Value;
+};
+
+class FilePathOption : public OptionValue
+{
+public:
+ FilePathOption(std::filesystem::path& Value) : Value(Value) {}
+ virtual void Print(std::string_view, zen::StringBuilderBase& StringBuilder) override
+ {
+ std::string Path = Value.string();
+ EscapeBackslash(Path);
+ StringBuilder.Append(fmt::format("\"{}\"", Path));
+ }
+ virtual void Parse(sol::object Object) override
+ {
+ std::string Str = Object.as<std::string>();
+ if (!Str.empty())
+ {
+ Value = MakeSafePath(Str);
+ }
+ }
+ std::filesystem::path& Value;
+};
+
+class BoolOption : public OptionValue
+{
+public:
+ BoolOption(bool& Value) : Value(Value) {}
+ virtual void Print(std::string_view, zen::StringBuilderBase& StringBuilder) override { StringBuilder.Append(Value ? "true" : "false"); }
+ virtual void Parse(sol::object Object) override { Value = Object.as<bool>(); }
+ bool& Value;
+};
+
+class CachePolicyOption : public OptionValue
+{
+public:
+ CachePolicyOption(UpstreamCachePolicy& Value) : Value(Value) {}
+ virtual void Print(std::string_view, zen::StringBuilderBase& StringBuilder) override
+ {
+ switch (Value)
+ {
+ case UpstreamCachePolicy::Read:
+ StringBuilder.Append("readonly");
+ break;
+ case UpstreamCachePolicy::Write:
+ StringBuilder.Append("writeonly");
+ break;
+ case UpstreamCachePolicy::Disabled:
+ StringBuilder.Append("disabled");
+ break;
+ case UpstreamCachePolicy::ReadWrite:
+ StringBuilder.Append("readwrite");
+ break;
+ default:
+ ZEN_ASSERT(false);
+ }
+ }
+ virtual void Parse(sol::object Object) override
+ {
+ std::string PolicyString = Object.as<std::string>();
+ if (PolicyString == "readonly")
+ {
+ Value = UpstreamCachePolicy::Read;
+ }
+ else if (PolicyString == "writeonly")
+ {
+ Value = UpstreamCachePolicy::Write;
+ }
+ else if (PolicyString == "disabled")
+ {
+ Value = UpstreamCachePolicy::Disabled;
+ }
+ else if (PolicyString == "readwrite")
+ {
+ Value = UpstreamCachePolicy::ReadWrite;
+ }
+ }
+ UpstreamCachePolicy& Value;
+};
+
+template<Integral T>
+class NumberOption : public OptionValue
+{
+public:
+ NumberOption(T& Value) : Value(Value) {}
+ virtual void Print(std::string_view, zen::StringBuilderBase& StringBuilder) override { StringBuilder.Append(fmt::format("{}", Value)); }
+ virtual void Parse(sol::object Object) override { Value = Object.as<T>(); }
+ T& Value;
+};
+
+class LuaContainerWriter
+{
+public:
+ LuaContainerWriter(zen::StringBuilderBase& StringBuilder, std::string_view Indent)
+ : StringBuilder(StringBuilder)
+ , InitialIndent(Indent.length())
+ , LocalIndent(Indent)
+ {
+ StringBuilder.Append("{\n");
+ LocalIndent.push_back('\t');
+ }
+ ~LuaContainerWriter()
+ {
+ LocalIndent.pop_back();
+ StringBuilder.Append(LocalIndent);
+ StringBuilder.Append("}");
+ }
+
+ void BeginContainer(std::string_view Name)
+ {
+ StringBuilder.Append(LocalIndent);
+ if (!Name.empty())
+ {
+ StringBuilder.Append(Name);
+ StringBuilder.Append(" = {\n");
+ }
+ else
+ {
+ StringBuilder.Append("{\n");
+ }
+ LocalIndent.push_back('\t');
+ }
+ void WriteValue(std::string_view Name, std::string_view Value)
+ {
+ if (Name.empty())
+ {
+ StringBuilder.Append(fmt::format("{}\"{}\",\n", LocalIndent, Value));
+ }
+ else
+ {
+ StringBuilder.Append(fmt::format("{}{} = \"{}\",\n", LocalIndent, Name, Value));
+ }
+ }
+ void EndContainer()
+ {
+ LocalIndent.pop_back();
+ StringBuilder.Append(LocalIndent);
+ StringBuilder.Append("}");
+ StringBuilder.Append(",\n");
+ }
+
+private:
+ zen::StringBuilderBase& StringBuilder;
+ const std::size_t InitialIndent;
+ std::string LocalIndent;
+};
+
+class StringArrayOption : public OptionValue
+{
+public:
+ StringArrayOption(std::vector<std::string>& Value) : Value(Value) {}
+ virtual void Print(std::string_view Indent, zen::StringBuilderBase& StringBuilder) override
+ {
+ if (Value.empty())
+ {
+ StringBuilder.Append("{}");
+ }
+ if (Value.size() == 1)
+ {
+ StringBuilder.Append(fmt::format("\"{}\"", Value[0]));
+ }
+ else
+ {
+ LuaContainerWriter Writer(StringBuilder, Indent);
+ for (std::string String : Value)
+ {
+ Writer.WriteValue("", String);
+ }
+ }
+ }
+ virtual void Parse(sol::object Object) override
+ {
+ if (Object.get_type() == sol::type::string)
+ {
+ Value.push_back(Object.as<std::string>());
+ }
+ else if (Object.get_type() == sol::type::table)
+ {
+ for (const auto& Kv : Object.as<sol::table>())
+ {
+ Value.push_back(Kv.second.as<std::string>());
+ }
+ }
+ }
+
+private:
+ std::vector<std::string>& Value;
+};
+
+class ZenAuthConfigOption : public OptionValue
+{
+public:
+ ZenAuthConfigOption(ZenAuthConfig& Value) : Value(Value) {}
+ virtual void Print(std::string_view Indent, zen::StringBuilderBase& StringBuilder) override
+ {
+ if (Value.OpenIdProviders.empty())
+ {
+ StringBuilder.Append("{}");
+ return;
+ }
+ LuaContainerWriter Writer(StringBuilder, Indent);
+ for (const ZenOpenIdProviderConfig& Config : Value.OpenIdProviders)
+ {
+ Writer.BeginContainer("");
+ {
+ Writer.WriteValue("name", Config.Name);
+ Writer.WriteValue("url", Config.Url);
+ Writer.WriteValue("clientid", Config.ClientId);
+ }
+ Writer.EndContainer();
+ }
+ }
+ virtual void Parse(sol::object Object) override
+ {
+ if (sol::optional<sol::table> OpenIdProviders = Object.as<sol::table>())
+ {
+ for (const auto& Kv : OpenIdProviders.value())
+ {
+ if (sol::optional<sol::table> OpenIdProvider = Kv.second.as<sol::table>())
+ {
+ std::string Name = OpenIdProvider.value().get_or("name", std::string("Default"));
+ std::string Url = OpenIdProvider.value().get_or("url", std::string());
+ std::string ClientId = OpenIdProvider.value().get_or("clientid", std::string());
+
+ Value.OpenIdProviders.push_back({.Name = std::move(Name), .Url = std::move(Url), .ClientId = std::move(ClientId)});
+ }
+ }
+ }
+ }
+ ZenAuthConfig& Value;
+};
+
+class ZenObjectStoreConfigOption : public OptionValue
+{
+public:
+ ZenObjectStoreConfigOption(ZenObjectStoreConfig& Value) : Value(Value) {}
+ virtual void Print(std::string_view Indent, zen::StringBuilderBase& StringBuilder) override
+ {
+ if (Value.Buckets.empty())
+ {
+ StringBuilder.Append("{}");
+ return;
+ }
+ LuaContainerWriter Writer(StringBuilder, Indent);
+ for (const ZenObjectStoreConfig::BucketConfig& Config : Value.Buckets)
+ {
+ Writer.BeginContainer("");
+ {
+ Writer.WriteValue("name", Config.Name);
+ std::string Directory = Config.Directory.string();
+ EscapeBackslash(Directory);
+ Writer.WriteValue("directory", Directory);
+ }
+ Writer.EndContainer();
+ }
+ }
+ virtual void Parse(sol::object Object) override
+ {
+ if (sol::optional<sol::table> Buckets = Object.as<sol::table>())
+ {
+ for (const auto& Kv : Buckets.value())
+ {
+ if (sol::optional<sol::table> Bucket = Kv.second.as<sol::table>())
+ {
+ std::string Name = Bucket.value().get_or("name", std::string("Default"));
+ std::string Directory = Bucket.value().get_or("directory", std::string());
+
+ Value.Buckets.push_back({.Name = std::move(Name), .Directory = MakeSafePath(Directory)});
+ }
+ }
+ }
+ }
+ ZenObjectStoreConfig& Value;
+};
+
+std::shared_ptr<OptionValue>
+MakeOption(std::string& Value)
+{
+ return std::make_shared<StringOption>(Value);
+};
+
+std::shared_ptr<OptionValue>
+MakeOption(std::filesystem::path& Value)
+{
+ return std::make_shared<FilePathOption>(Value);
+};
+
+template<Integral T>
+std::shared_ptr<OptionValue>
+MakeOption(T& Value)
+{
+ return std::make_shared<NumberOption<T>>(Value);
+};
+
+std::shared_ptr<OptionValue>
+MakeOption(bool& Value)
+{
+ return std::make_shared<BoolOption>(Value);
+};
+
+std::shared_ptr<OptionValue>
+MakeOption(UpstreamCachePolicy& Value)
+{
+ return std::make_shared<CachePolicyOption>(Value);
+};
+
+std::shared_ptr<OptionValue>
+MakeOption(std::vector<std::string>& Value)
+{
+ return std::make_shared<StringArrayOption>(Value);
+};
+
+std::shared_ptr<OptionValue>
+MakeOption(ZenAuthConfig& Value)
+{
+ return std::make_shared<ZenAuthConfigOption>(Value);
+};
+
+std::shared_ptr<OptionValue>
+MakeOption(ZenObjectStoreConfig& Value)
+{
+ return std::make_shared<ZenObjectStoreConfigOption>(Value);
+};
+
+struct Option
+{
+ std::string CommandLineOptionName;
+ TOptionValue Value;
+};
+
+struct Options
+{
+public:
+ template<typename T>
+ void AddOption(std::string_view Key, T& Value, std::string_view CommandLineOptionName = "")
+ {
+ OptionMap.insert_or_assign(std::string(Key),
+ Option{.CommandLineOptionName = std::string(CommandLineOptionName), .Value = MakeOption(Value)});
+ };
+
+ void Parse(const std::filesystem::path& Path, const cxxopts::ParseResult& CmdLineResult)
+ {
+ zen::IoBuffer LuaScript = zen::IoBufferBuilder::MakeFromFile(Path);
+
+ if (LuaScript)
+ {
+ sol::state lua;
+
+ lua.open_libraries(sol::lib::base);
+
+ lua.set_function("getenv", [&](const std::string env) -> sol::object {
+#if ZEN_PLATFORM_WINDOWS
+ std::wstring EnvVarValue;
+ size_t RequiredSize = 0;
+ std::wstring EnvWide = zen::Utf8ToWide(env);
+ _wgetenv_s(&RequiredSize, nullptr, 0, EnvWide.c_str());
+
+ if (RequiredSize == 0)
+ return sol::make_object(lua, sol::lua_nil);
+
+ EnvVarValue.resize(RequiredSize);
+ _wgetenv_s(&RequiredSize, EnvVarValue.data(), RequiredSize, EnvWide.c_str());
+ return sol::make_object(lua, zen::WideToUtf8(EnvVarValue.c_str()));
+#elif ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC
+ char* EnvVariable = getenv(env.c_str());
+ if (EnvVariable == nullptr)
+ {
+ return sol::make_object(lua, sol::lua_nil);
+ }
+ return sol::make_object(lua, EnvVariable);
+#else
+ ZEN_UNUSED(env);
+ return sol::make_object(lua, sol::lua_nil);
+#endif
+ });
+
+ try
+ {
+ sol::load_result config = lua.load(std::string_view((const char*)LuaScript.Data(), LuaScript.Size()), "zen_cfg");
+
+ if (!config.valid())
+ {
+ sol::error err = config;
+
+ std::string ErrorString = sol::to_string(config.status());
+
+ throw std::runtime_error(fmt::format("{} error: {}", ErrorString, err.what()));
+ }
+
+ config();
+ }
+ catch (std::exception& e)
+ {
+ throw std::runtime_error(fmt::format("failed to load config script ('{}'): {}", Path, e.what()).c_str());
+ }
+
+ Parse(lua, CmdLineResult);
+ }
+ }
+
+ void Parse(const sol::state& LuaState, const cxxopts::ParseResult& CmdLineResult)
+ {
+ for (auto It : LuaState)
+ {
+ sol::object Key = It.first;
+ sol::type KeyType = Key.get_type();
+ if (KeyType == sol::type::string)
+ {
+ sol::type ValueType = It.second.get_type();
+ switch (ValueType)
+ {
+ case sol::type::table:
+ {
+ std::string Name = Key.as<std::string>();
+ if (Name.starts_with("_"))
+ {
+ continue;
+ }
+ if (Name == "base")
+ {
+ continue;
+ }
+ Traverse(It.second.as<sol::table>(), Name, CmdLineResult);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ void Touch(std::string_view Key) { UsedKeys.insert(std::string(Key)); }
+
+ void Print(zen::StringBuilderBase& SB, const cxxopts::ParseResult& CmdLineResult)
+ {
+ for (auto It : OptionMap)
+ {
+ if (CmdLineResult.count(It.second.CommandLineOptionName) != 0)
+ {
+ UsedKeys.insert(It.first);
+ }
+ }
+
+ std::vector<std::string> SortedKeys(UsedKeys.begin(), UsedKeys.end());
+ std::sort(SortedKeys.begin(), SortedKeys.end());
+ auto GetTablePath = [](const std::string& Key) -> std::vector<std::string> {
+ std::vector<std::string> Path;
+ zen::ForEachStrTok(Key, '.', [&Path](std::string_view Part) {
+ Path.push_back(std::string(Part));
+ return true;
+ });
+ return Path;
+ };
+ std::vector<std::string> CurrentTablePath;
+ std::string Indent;
+ auto It = SortedKeys.begin();
+ for (const std::string& Key : SortedKeys)
+ {
+ std::vector<std::string> KeyPath = GetTablePath(Key);
+ std::string Name = KeyPath.back();
+ KeyPath.pop_back();
+ if (CurrentTablePath != KeyPath)
+ {
+ size_t EqualCount = 0;
+ while (EqualCount < CurrentTablePath.size() && EqualCount < KeyPath.size() &&
+ CurrentTablePath[EqualCount] == KeyPath[EqualCount])
+ {
+ EqualCount++;
+ }
+ while (CurrentTablePath.size() > EqualCount)
+ {
+ CurrentTablePath.pop_back();
+ Indent.pop_back();
+ SB.Append(Indent);
+ SB.Append("}");
+ if (CurrentTablePath.size() == EqualCount && !Indent.empty() && KeyPath.size() >= EqualCount)
+ {
+ SB.Append(",");
+ }
+ SB.Append("\n");
+ if (Indent.empty())
+ {
+ SB.Append("\n");
+ }
+ }
+ while (EqualCount < KeyPath.size())
+ {
+ SB.Append(Indent);
+ SB.Append(KeyPath[EqualCount]);
+ SB.Append(" = {\n");
+ Indent.push_back('\t');
+ CurrentTablePath.push_back(KeyPath[EqualCount]);
+ EqualCount++;
+ }
+ }
+
+ SB.Append(Indent);
+ SB.Append(Name);
+ SB.Append(" = ");
+ OptionMap[Key].Value->Print(Indent, SB);
+ SB.Append(",\n");
+ }
+ while (!CurrentTablePath.empty())
+ {
+ Indent.pop_back();
+ SB.Append(Indent);
+ SB.Append("}\n");
+ CurrentTablePath.pop_back();
+ }
+ }
+
+private:
+ void Traverse(sol::table Table, std::string_view PathPrefix, const cxxopts::ParseResult& CmdLineResult)
+ {
+ for (auto It : Table)
+ {
+ sol::object Key = It.first;
+ sol::type KeyType = Key.get_type();
+ if (KeyType == sol::type::string || KeyType == sol::type::number)
+ {
+ sol::type ValueType = It.second.get_type();
+ switch (ValueType)
+ {
+ case sol::type::table:
+ case sol::type::string:
+ case sol::type::number:
+ case sol::type::boolean:
+ {
+ std::string Name = Key.as<std::string>();
+ if (Name.starts_with("_"))
+ {
+ continue;
+ }
+ Name = std::string(PathPrefix) + "." + Key.as<std::string>();
+ auto OptionIt = OptionMap.find(Name);
+ if (OptionIt != OptionMap.end())
+ {
+ UsedKeys.insert(Name);
+ if (CmdLineResult.count(OptionIt->second.CommandLineOptionName) != 0)
+ {
+ continue;
+ }
+ OptionIt->second.Value->Parse(It.second);
+ continue;
+ }
+ if (ValueType == sol::type::table)
+ {
+ if (Name == "base")
+ {
+ continue;
+ }
+ Traverse(It.second.as<sol::table>(), Name, CmdLineResult);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ std::unordered_map<std::string, Option> OptionMap;
+ std::unordered_set<std::string> UsedKeys;
+};
+
+} // namespace LuaConfig
+
+void
+ParseConfigFile(const std::filesystem::path& Path, ZenServerOptions& ServerOptions, const cxxopts::ParseResult& CmdLineResult)
+{
+ using namespace std::literals;
+
+ LuaConfig::Options LuaOptions;
+
+ ////// server
+ LuaOptions.AddOption("server.dedicated"sv, ServerOptions.IsDedicated, "dedicated"sv);
+ LuaOptions.AddOption("server.logid"sv, ServerOptions.LogId, "log-id"sv);
+ LuaOptions.AddOption("server.sentry.disable"sv, ServerOptions.NoSentry, "no-sentry"sv);
+ LuaOptions.AddOption("server.sentry.allowpersonalinfo"sv, ServerOptions.SentryAllowPII, "sentry-allow-personal-info"sv);
+ LuaOptions.AddOption("server.datadir"sv, ServerOptions.DataDir, "data-dir"sv);
+ LuaOptions.AddOption("server.contentdir"sv, ServerOptions.ContentDir, "content-dir"sv);
+ LuaOptions.AddOption("server.abslog"sv, ServerOptions.AbsLogFile, "abslog"sv);
+ LuaOptions.AddOption("server.debug"sv, ServerOptions.IsDebug, "debug"sv);
+
+ ////// objectstore
+ LuaOptions.AddOption("server.objectstore.enabled"sv, ServerOptions.ObjectStoreEnabled, "objectstore-enabled"sv);
+ LuaOptions.AddOption("server.objectstore.buckets"sv, ServerOptions.ObjectStoreConfig);
+
+ ////// network
+ LuaOptions.AddOption("network.httpserverclass"sv, ServerOptions.HttpServerClass, "http"sv);
+ LuaOptions.AddOption("network.port"sv, ServerOptions.BasePort, "port"sv);
+ LuaOptions.AddOption("network.websocket.port"sv, ServerOptions.WebSocketPort, "websocket-port"sv);
+ LuaOptions.AddOption("network.websocket.threadcount"sv, ServerOptions.WebSocketThreads, "websocket-threads"sv);
+
+ ////// trace
+ LuaOptions.AddOption("trace.host"sv, ServerOptions.TraceHost, "tracehost"sv);
+ LuaOptions.AddOption("trace.file"sv, ServerOptions.TraceFile, "tracefile"sv);
+
+ ////// cache
+ LuaOptions.AddOption("cache.enable"sv, ServerOptions.StructuredCacheEnabled);
+
+ ////// cache.upstream
+ LuaOptions.AddOption("cache.upstream.policy"sv, ServerOptions.UpstreamCacheConfig.CachePolicy, "upstream-cache-policy"sv);
+ LuaOptions.AddOption("cache.upstream.upstreamthreadcount"sv,
+ ServerOptions.UpstreamCacheConfig.UpstreamThreadCount,
+ "upstream-thread-count"sv);
+ LuaOptions.AddOption("cache.upstream.connecttimeoutms"sv,
+ ServerOptions.UpstreamCacheConfig.ConnectTimeoutMilliseconds,
+ "upstream-connect-timeout-ms"sv);
+ LuaOptions.AddOption("cache.upstream.timeoutms"sv, ServerOptions.UpstreamCacheConfig.TimeoutMilliseconds, "upstream-timeout-ms"sv);
+
+ ////// cache.upstream.jupiter
+ LuaOptions.AddOption("cache.upstream.jupiter.name"sv, ServerOptions.UpstreamCacheConfig.JupiterConfig.Name);
+ LuaOptions.AddOption("cache.upstream.jupiter.url"sv, ServerOptions.UpstreamCacheConfig.JupiterConfig.Url, "upstream-jupiter-url"sv);
+ LuaOptions.AddOption("cache.upstream.jupiter.oauthprovider"sv,
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthUrl,
+ "upstream-jupiter-oauth-url"sv);
+ LuaOptions.AddOption("cache.upstream.jupiter.oauthclientid"sv,
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthClientId,
+ "upstream-jupiter-oauth-clientid");
+ LuaOptions.AddOption("cache.upstream.jupiter.oauthclientsecret"sv,
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthClientSecret,
+ "upstream-jupiter-oauth-clientsecret"sv);
+ LuaOptions.AddOption("cache.upstream.jupiter.openidprovider"sv,
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.OpenIdProvider,
+ "upstream-jupiter-openid-provider"sv);
+ LuaOptions.AddOption("cache.upstream.jupiter.token"sv,
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.AccessToken,
+ "upstream-jupiter-token"sv);
+ LuaOptions.AddOption("cache.upstream.jupiter.namespace"sv,
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.Namespace,
+ "upstream-jupiter-namespace"sv);
+ LuaOptions.AddOption("cache.upstream.jupiter.ddcnamespace"sv,
+ ServerOptions.UpstreamCacheConfig.JupiterConfig.DdcNamespace,
+ "upstream-jupiter-namespace-ddc"sv);
+
+ ////// cache.upstream.zen
+ // LuaOptions.AddOption("cache.upstream.zen"sv, ServerOptions.UpstreamCacheConfig.ZenConfig);
+ LuaOptions.AddOption("cache.upstream.zen.name"sv, ServerOptions.UpstreamCacheConfig.ZenConfig.Name);
+ LuaOptions.AddOption("cache.upstream.zen.dns"sv, ServerOptions.UpstreamCacheConfig.ZenConfig.Dns);
+ LuaOptions.AddOption("cache.upstream.zen.url"sv, ServerOptions.UpstreamCacheConfig.ZenConfig.Urls);
+
+ ////// exec
+ LuaOptions.AddOption("exec.enable"sv, ServerOptions.ExecServiceEnabled);
+
+ ////// compute
+ LuaOptions.AddOption("compute.enable"sv, ServerOptions.ComputeServiceEnabled);
+
+ LuaOptions.AddOption("compute.upstream.horde.name"sv, ServerOptions.UpstreamCacheConfig.HordeConfig.Name);
+ LuaOptions.AddOption("compute.upstream.horde.url"sv, ServerOptions.UpstreamCacheConfig.HordeConfig.Url, "upstream-horde-url"sv);
+ LuaOptions.AddOption("compute.upstream.horde.oauthprovider"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.OAuthUrl,
+ "upstream-horde-oauth-url"sv);
+ LuaOptions.AddOption("compute.upstream.horde.oauthclientid"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.OAuthClientId,
+ "upstream-horde-oauth-clientid"sv);
+ LuaOptions.AddOption("compute.upstream.horde.oauthclientsecret"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.OAuthClientSecret,
+ "upstream-horde-oauth-clientsecret"sv);
+ LuaOptions.AddOption("compute.upstream.horde.openidprovider"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.OpenIdProvider,
+ "upstream-horde-openid-provider"sv);
+ LuaOptions.AddOption("compute.upstream.horde.token"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.AccessToken,
+ "upstream-horde-token"sv);
+ LuaOptions.AddOption("compute.upstream.horde.cluster"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.Cluster,
+ "upstream-horde-cluster"sv);
+ LuaOptions.AddOption("compute.upstream.horde.namespace"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.Namespace,
+ "upstream-horde-namespace"sv);
+
+ ////// compute storage
+ LuaOptions.AddOption("compute.upstream.storage.name"sv, ServerOptions.UpstreamCacheConfig.HordeConfig.Name);
+ LuaOptions.AddOption("compute.upstream.storage.url"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.Url,
+ "upstream-horde-storage-oauth-url"sv);
+ LuaOptions.AddOption("compute.upstream.storage.oauthprovider"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOAuthUrl,
+ "upstream-horde-storage-oauth-url"sv);
+ LuaOptions.AddOption("compute.upstream.storage.oauthclientid"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOAuthClientId,
+ "upstream-horde-storage-oauth-clientid"sv);
+ LuaOptions.AddOption("compute.upstream.storage.oauthclientsecret"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOAuthClientSecret,
+ "upstream-horde-storage-oauth-clientsecret"sv);
+ LuaOptions.AddOption("compute.upstream.storage.openidprovider"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOpenIdProvider,
+ "upstream-horde-storage-openid-provider"sv);
+ LuaOptions.AddOption("compute.upstream.storage.token"sv,
+ ServerOptions.UpstreamCacheConfig.HordeConfig.StorageAccessToken,
+ "upstream-horde-storage-token"sv);
+
+ LuaOptions.AddOption("gc.enabled"sv, ServerOptions.GcConfig.Enabled, "gc-enabled"sv);
+
+ LuaOptions.AddOption("gc.monitorintervalseconds"sv, ServerOptions.GcConfig.MonitorIntervalSeconds, "gc-monitor-interval-seconds"sv);
+ LuaOptions.AddOption("gc.intervalseconds"sv, ServerOptions.GcConfig.IntervalSeconds, "gc-interval-seconds"sv);
+ LuaOptions.AddOption("gc.collectsmallobjects"sv, ServerOptions.GcConfig.CollectSmallObjects, "gc-small-objects"sv);
+ LuaOptions.AddOption("gc.diskreservesize"sv, ServerOptions.GcConfig.DiskReserveSize, "disk-reserve-size"sv);
+ LuaOptions.AddOption("gc.disksizesoftlimit"sv, ServerOptions.GcConfig.DiskSizeSoftLimit, "gc-disksize-softlimit"sv);
+ LuaOptions.AddOption("gc.lowdiskspacethreshold"sv,
+ ServerOptions.GcConfig.MinimumFreeDiskSpaceToAllowWrites,
+ "gc-low-diskspace-threshold"sv);
+
+ ////// gc
+ LuaOptions.AddOption("gc.cache.maxdurationseconds"sv, ServerOptions.GcConfig.Cache.MaxDurationSeconds, "gc-cache-duration-seconds"sv);
+
+ ////// security
+ LuaOptions.AddOption("security.encryptionaeskey"sv, ServerOptions.EncryptionKey, "encryption-aes-key"sv);
+ LuaOptions.AddOption("security.encryptionaesiv"sv, ServerOptions.EncryptionIV, "encryption-aes-iv"sv);
+ LuaOptions.AddOption("security.openidproviders"sv, ServerOptions.AuthConfig);
+
+ LuaOptions.Parse(Path, CmdLineResult);
+
+ // These have special command line processing so we make sure we export them if they were configured on command line
+ if (!ServerOptions.AuthConfig.OpenIdProviders.empty())
+ {
+ LuaOptions.Touch("security.openidproviders"sv);
+ }
+ if (!ServerOptions.ObjectStoreConfig.Buckets.empty())
+ {
+ LuaOptions.Touch("server.objectstore.buckets"sv);
+ }
+}
void
ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions)
@@ -571,25 +1359,6 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions)
exit(0);
}
- auto MakeSafePath = [](const std::string& Path) {
-#if ZEN_PLATFORM_WINDOWS
- if (Path.empty())
- {
- return Path;
- }
-
- std::string FixedPath = Path;
- std::replace(FixedPath.begin(), FixedPath.end(), '/', '\\');
- if (!FixedPath.starts_with("\\\\?\\"))
- {
- FixedPath.insert(0, "\\\\?\\");
- }
- return FixedPath;
-#else
- return Path;
-#endif
- };
-
ServerOptions.DataDir = MakeSafePath(DataDir);
ServerOptions.ContentDir = MakeSafePath(ContentDir);
ServerOptions.AbsLogFile = MakeSafePath(AbsLogFile);
@@ -611,11 +1380,11 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions)
if (!ServerOptions.ConfigFile.empty())
{
- ParseConfigFile(ServerOptions.ConfigFile, ServerOptions);
+ ParseConfigFile(ServerOptions.ConfigFile, ServerOptions, result);
}
else
{
- ParseConfigFile(ServerOptions.DataDir / "zen_cfg.lua", ServerOptions);
+ ParseConfigFile(ServerOptions.DataDir / "zen_cfg.lua", ServerOptions, result);
}
ValidateOptions(ServerOptions);
@@ -643,281 +1412,3 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions)
ServerOptions.AbsLogFile = ServerOptions.DataDir / "logs" / "zenserver.log";
}
}
-
-void
-ParseConfigFile(const std::filesystem::path& Path, ZenServerOptions& ServerOptions)
-{
- zen::IoBuffer LuaScript = zen::IoBufferBuilder::MakeFromFile(Path);
-
- if (LuaScript)
- {
- sol::state lua;
-
- lua.open_libraries(sol::lib::base);
-
- lua.set_function("getenv", [&](const std::string env) -> sol::object {
-#if ZEN_PLATFORM_WINDOWS
- std::wstring EnvVarValue;
- size_t RequiredSize = 0;
- std::wstring EnvWide = zen::Utf8ToWide(env);
- _wgetenv_s(&RequiredSize, nullptr, 0, EnvWide.c_str());
-
- if (RequiredSize == 0)
- return sol::make_object(lua, sol::lua_nil);
-
- EnvVarValue.resize(RequiredSize);
- _wgetenv_s(&RequiredSize, EnvVarValue.data(), RequiredSize, EnvWide.c_str());
- return sol::make_object(lua, zen::WideToUtf8(EnvVarValue.c_str()));
-#else
- ZEN_UNUSED(env);
- return sol::make_object(lua, sol::lua_nil);
-#endif
- });
-
- try
- {
- sol::load_result config = lua.load(std::string_view((const char*)LuaScript.Data(), LuaScript.Size()), "zen_cfg");
-
- if (!config.valid())
- {
- sol::error err = config;
-
- std::string ErrorString = sol::to_string(config.status());
-
- throw std::runtime_error(fmt::format("{} error: {}", ErrorString, err.what()));
- }
-
- config();
- }
- catch (std::exception& e)
- {
- throw std::runtime_error(fmt::format("failed to load config script ('{}'): {}", Path, e.what()).c_str());
- }
-
- if (sol::optional<sol::table> ServerConfig = lua["server"])
- {
- if (ServerOptions.DataDir.empty())
- {
- if (sol::optional<std::string> Opt = ServerConfig.value()["datadir"])
- {
- ServerOptions.DataDir = Opt.value();
- }
- }
-
- if (ServerOptions.ContentDir.empty())
- {
- if (sol::optional<std::string> Opt = ServerConfig.value()["contentdir"])
- {
- ServerOptions.ContentDir = Opt.value();
- }
- }
-
- if (ServerOptions.AbsLogFile.empty())
- {
- if (sol::optional<std::string> Opt = ServerConfig.value()["abslog"])
- {
- ServerOptions.AbsLogFile = Opt.value();
- }
- }
-
- ServerOptions.IsDebug = ServerConfig->get_or("debug", ServerOptions.IsDebug);
- }
-
- if (sol::optional<sol::table> NetworkConfig = lua["network"])
- {
- if (sol::optional<std::string> Opt = NetworkConfig.value()["httpserverclass"])
- {
- ServerOptions.HttpServerClass = Opt.value();
- }
-
- ServerOptions.BasePort = NetworkConfig->get_or<int>("port", ServerOptions.BasePort);
- }
-
- auto UpdateStringValueFromConfig = [](const sol::table& Table, std::string_view Key, std::string& OutValue) {
- // Update the specified config value unless it has been set, i.e. from command line
- if (auto MaybeValue = Table.get<sol::optional<std::string>>(Key); MaybeValue.has_value() && OutValue.empty())
- {
- OutValue = MaybeValue.value();
- }
- };
-
- if (sol::optional<sol::table> StructuredCacheConfig = lua["cache"])
- {
- ServerOptions.StructuredCacheEnabled = StructuredCacheConfig->get_or("enable", ServerOptions.StructuredCacheEnabled);
-
- if (auto UpstreamConfig = StructuredCacheConfig->get<sol::optional<sol::table>>("upstream"))
- {
- std::string Policy = UpstreamConfig->get_or("policy", std::string());
- ServerOptions.UpstreamCacheConfig.CachePolicy = ParseUpstreamCachePolicy(Policy);
- ServerOptions.UpstreamCacheConfig.UpstreamThreadCount =
- UpstreamConfig->get_or("upstreamthreadcount", ServerOptions.UpstreamCacheConfig.UpstreamThreadCount);
-
- if (auto JupiterConfig = UpstreamConfig->get<sol::optional<sol::table>>("jupiter"))
- {
- UpdateStringValueFromConfig(JupiterConfig.value(),
- std::string_view("name"),
- ServerOptions.UpstreamCacheConfig.JupiterConfig.Name);
- UpdateStringValueFromConfig(JupiterConfig.value(),
- std::string_view("url"),
- ServerOptions.UpstreamCacheConfig.JupiterConfig.Url);
- UpdateStringValueFromConfig(JupiterConfig.value(),
- std::string_view("oauthprovider"),
- ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthUrl);
- UpdateStringValueFromConfig(JupiterConfig.value(),
- std::string_view("oauthclientid"),
- ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthClientId);
- UpdateStringValueFromConfig(JupiterConfig.value(),
- std::string_view("oauthclientsecret"),
- ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthClientSecret);
- UpdateStringValueFromConfig(JupiterConfig.value(),
- std::string_view("openidprovider"),
- ServerOptions.UpstreamCacheConfig.JupiterConfig.OpenIdProvider);
- UpdateStringValueFromConfig(JupiterConfig.value(),
- std::string_view("token"),
- ServerOptions.UpstreamCacheConfig.JupiterConfig.AccessToken);
- UpdateStringValueFromConfig(JupiterConfig.value(),
- std::string_view("namespace"),
- ServerOptions.UpstreamCacheConfig.JupiterConfig.Namespace);
- UpdateStringValueFromConfig(JupiterConfig.value(),
- std::string_view("ddcnamespace"),
- ServerOptions.UpstreamCacheConfig.JupiterConfig.DdcNamespace);
- };
-
- if (auto ZenConfig = UpstreamConfig->get<sol::optional<sol::table>>("zen"))
- {
- ServerOptions.UpstreamCacheConfig.ZenConfig.Name = ZenConfig.value().get_or("name", std::string("Zen"));
-
- if (auto Url = ZenConfig.value().get<sol::optional<std::string>>("url"))
- {
- ServerOptions.UpstreamCacheConfig.ZenConfig.Urls.push_back(Url.value());
- }
- else if (auto Urls = ZenConfig.value().get<sol::optional<sol::table>>("url"))
- {
- for (const auto& Kv : Urls.value())
- {
- ServerOptions.UpstreamCacheConfig.ZenConfig.Urls.push_back(Kv.second.as<std::string>());
- }
- }
-
- if (auto Dns = ZenConfig.value().get<sol::optional<std::string>>("dns"))
- {
- ServerOptions.UpstreamCacheConfig.ZenConfig.Dns.push_back(Dns.value());
- }
- else if (auto DnsArray = ZenConfig.value().get<sol::optional<sol::table>>("dns"))
- {
- for (const auto& Kv : DnsArray.value())
- {
- ServerOptions.UpstreamCacheConfig.ZenConfig.Dns.push_back(Kv.second.as<std::string>());
- }
- }
- }
- }
- }
-
- if (sol::optional<sol::table> ExecConfig = lua["exec"])
- {
- ServerOptions.ExecServiceEnabled = ExecConfig->get_or("enable", ServerOptions.ExecServiceEnabled);
- }
-
- if (sol::optional<sol::table> ComputeConfig = lua["compute"])
- {
- ServerOptions.ComputeServiceEnabled = ComputeConfig->get_or("enable", ServerOptions.ComputeServiceEnabled);
-
- if (auto UpstreamConfig = ComputeConfig->get<sol::optional<sol::table>>("upstream"))
- {
- if (auto HordeConfig = UpstreamConfig->get<sol::optional<sol::table>>("horde"))
- {
- UpdateStringValueFromConfig(HordeConfig.value(),
- std::string_view("name"),
- ServerOptions.UpstreamCacheConfig.HordeConfig.Name);
- UpdateStringValueFromConfig(HordeConfig.value(),
- std::string_view("url"),
- ServerOptions.UpstreamCacheConfig.HordeConfig.Url);
- UpdateStringValueFromConfig(HordeConfig.value(),
- std::string_view("oauthprovider"),
- ServerOptions.UpstreamCacheConfig.HordeConfig.OAuthUrl);
- UpdateStringValueFromConfig(HordeConfig.value(),
- std::string_view("oauthclientid"),
- ServerOptions.UpstreamCacheConfig.HordeConfig.OAuthClientId);
- UpdateStringValueFromConfig(HordeConfig.value(),
- std::string_view("oauthclientsecret"),
- ServerOptions.UpstreamCacheConfig.HordeConfig.OAuthClientSecret);
- UpdateStringValueFromConfig(HordeConfig.value(),
- std::string_view("openidprovider"),
- ServerOptions.UpstreamCacheConfig.HordeConfig.OpenIdProvider);
- UpdateStringValueFromConfig(HordeConfig.value(),
- std::string_view("token"),
- ServerOptions.UpstreamCacheConfig.HordeConfig.AccessToken);
- UpdateStringValueFromConfig(HordeConfig.value(),
- std::string_view("cluster"),
- ServerOptions.UpstreamCacheConfig.HordeConfig.Cluster);
- UpdateStringValueFromConfig(HordeConfig.value(),
- std::string_view("namespace"),
- ServerOptions.UpstreamCacheConfig.HordeConfig.Namespace);
- };
-
- if (auto StorageConfig = UpstreamConfig->get<sol::optional<sol::table>>("storage"))
- {
- UpdateStringValueFromConfig(StorageConfig.value(),
- std::string_view("url"),
- ServerOptions.UpstreamCacheConfig.HordeConfig.StorageUrl);
- UpdateStringValueFromConfig(StorageConfig.value(),
- std::string_view("oauthprovider"),
- ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOAuthUrl);
- UpdateStringValueFromConfig(StorageConfig.value(),
- std::string_view("oauthclientid"),
- ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOAuthClientId);
- UpdateStringValueFromConfig(StorageConfig.value(),
- std::string_view("oauthclientsecret"),
- ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOAuthClientSecret);
- UpdateStringValueFromConfig(StorageConfig.value(),
- std::string_view("openidprovider"),
- ServerOptions.UpstreamCacheConfig.HordeConfig.StorageOpenIdProvider);
- UpdateStringValueFromConfig(StorageConfig.value(),
- std::string_view("token"),
- ServerOptions.UpstreamCacheConfig.HordeConfig.StorageAccessToken);
- };
- }
- }
-
- if (sol::optional<sol::table> GcConfig = lua["gc"])
- {
- ServerOptions.GcConfig.MonitorIntervalSeconds = GcConfig.value().get_or("monitorintervalseconds", 30);
- ServerOptions.GcConfig.IntervalSeconds = GcConfig.value().get_or("intervalseconds", 0);
- ServerOptions.GcConfig.DiskReserveSize = GcConfig.value().get_or("diskreservesize", uint64_t(1u << 28));
- ServerOptions.GcConfig.DiskSizeSoftLimit = GcConfig.value().get_or("disksizesoftlimit", 0);
- ServerOptions.GcConfig.MinimumFreeDiskSpaceToAllowWrites = GcConfig.value().get_or("lowdiskspacethreshold", 0);
-
- if (sol::optional<sol::table> CacheGcConfig = GcConfig.value()["cache"])
- {
- ServerOptions.GcConfig.Cache.MaxDurationSeconds = CacheGcConfig.value().get_or("maxdurationseconds", int32_t(0));
- }
- if (sol::optional<sol::table> CacheGcConfig = GcConfig.value()["projectstore"])
- {
- ServerOptions.GcConfig.ProjectStore.MaxDurationSeconds = CacheGcConfig.value().get_or("maxdurationseconds", int32_t(0));
- }
- }
-
- if (sol::optional<sol::table> SecurityConfig = lua["security"])
- {
- if (sol::optional<sol::table> OpenIdProviders = SecurityConfig.value()["openidproviders"])
- {
- for (const auto& Kv : OpenIdProviders.value())
- {
- if (sol::optional<sol::table> OpenIdProvider = Kv.second.as<sol::table>())
- {
- std::string Name = OpenIdProvider.value().get_or("name", std::string("Default"));
- std::string Url = OpenIdProvider.value().get_or("url", std::string());
- std::string ClientId = OpenIdProvider.value().get_or("clientid", std::string());
-
- ServerOptions.AuthConfig.OpenIdProviders.push_back(
- {.Name = std::move(Name), .Url = std::move(Url), .ClientId = std::move(ClientId)});
- }
- }
- }
-
- ServerOptions.EncryptionKey = SecurityConfig.value().get_or("encryptionaeskey", std::string());
- ServerOptions.EncryptionIV = SecurityConfig.value().get_or("encryptionaesiv", std::string());
- }
- }
-}