// Copyright Epic Games, Inc. All Rights Reserved. #include "storageconfig.h" #include #include #include #include #include "config/luaconfig.h" ZEN_THIRD_PARTY_INCLUDES_START #include #include #include ZEN_THIRD_PARTY_INCLUDES_END namespace zen { void ZenStorageServerConfigurator::ValidateOptions() { auto& ServerOptions = m_ServerOptions; if (ServerOptions.EncryptionKey.empty() == false) { const auto Key = AesKey256Bit::FromString(ServerOptions.EncryptionKey); if (Key.IsValid() == false) { throw OptionParseException(fmt::format("'--encryption-aes-key' ('{}') is malformed", ServerOptions.EncryptionKey), {}); } } if (ServerOptions.EncryptionIV.empty() == false) { const auto IV = AesIV128Bit::FromString(ServerOptions.EncryptionIV); if (IV.IsValid() == false) { throw OptionParseException(fmt::format("'--encryption-aes-iv' ('{}') is malformed", ServerOptions.EncryptionIV), {}); } } if (ServerOptions.HttpConfig.ForceLoopback && ServerOptions.IsDedicated) { throw OptionParseException("'--dedicated' conflicts with '--http-forceloopback'", {}); } if (ServerOptions.GcConfig.AttachmentPassCount > ZenGcConfig::GcMaxAttachmentPassCount) { throw OptionParseException(fmt::format("'--gc-attachment-passes' ('{}') is invalid, maximum is {}.", ServerOptions.GcConfig.AttachmentPassCount, ZenGcConfig::GcMaxAttachmentPassCount), {}); } if (ServerOptions.GcConfig.UseGCV2 == false) { ZEN_WARN("'--gc-v2=false' is deprecated, reverting to '--gc-v2=true'"); ServerOptions.GcConfig.UseGCV2 = true; } } class ZenStructuredCacheBucketsConfigOption : public LuaConfig::OptionValue { public: ZenStructuredCacheBucketsConfigOption(std::vector>& Value) : Value(Value) {} virtual void Print(std::string_view Indent, StringBuilderBase& StringBuilder) override { if (Value.empty()) { StringBuilder.Append("{}"); return; } LuaConfig::LuaContainerWriter Writer(StringBuilder, Indent); for (const std::pair& Bucket : Value) { Writer.BeginContainer(""); { Writer.WriteValue("name", Bucket.first); const ZenStructuredCacheBucketConfig& BucketConfig = Bucket.second; Writer.WriteValue("maxblocksize", fmt::format("{}", BucketConfig.MaxBlockSize)); Writer.BeginContainer("memlayer"); { Writer.WriteValue("sizethreshold", fmt::format("{}", BucketConfig.MemCacheSizeThreshold)); } Writer.EndContainer(); Writer.WriteValue("payloadalignment", fmt::format("{}", BucketConfig.PayloadAlignment)); Writer.WriteValue("largeobjectthreshold", fmt::format("{}", BucketConfig.PayloadAlignment)); Writer.WriteValue("limitoverwrites", fmt::format("{}", BucketConfig.LimitOverwrites)); } Writer.EndContainer(); } } virtual void Parse(sol::object Object) override { if (sol::optional Buckets = Object.as()) { for (const auto& Kv : Buckets.value()) { if (sol::optional Bucket = Kv.second.as()) { ZenStructuredCacheBucketConfig BucketConfig; std::string Name = Kv.first.as(); if (Name.empty()) { throw OptionParseException("Cache bucket option must have a name.", {}); } const uint64_t MaxBlockSize = Bucket.value().get_or("maxblocksize", BucketConfig.MaxBlockSize); if (MaxBlockSize == 0) { throw OptionParseException( fmt::format("'maxblocksize' option for cache bucket '{}' is invalid. It must be non-zero.", Name), {}); } BucketConfig.MaxBlockSize = MaxBlockSize; if (sol::optional Memlayer = Bucket.value().get_or("memlayer", sol::table())) { const uint64_t MemCacheSizeThreshold = Bucket.value().get_or("sizethreshold", BucketConfig.MemCacheSizeThreshold); if (MemCacheSizeThreshold == 0) { throw OptionParseException( fmt::format("'memlayer.sizethreshold' option for cache bucket '{}' is invalid. It must be non-zero.", Name), {}); } BucketConfig.MemCacheSizeThreshold = Bucket.value().get_or("sizethreshold", BucketConfig.MemCacheSizeThreshold); } const uint32_t PayloadAlignment = Bucket.value().get_or("payloadalignment", BucketConfig.PayloadAlignment); if (PayloadAlignment == 0 || !IsPow2(PayloadAlignment)) { throw OptionParseException( fmt::format( "'payloadalignment' option for cache bucket '{}' is invalid. It needs to be non-zero and a power of two.", Name), {}); } BucketConfig.PayloadAlignment = PayloadAlignment; const uint64_t LargeObjectThreshold = Bucket.value().get_or("largeobjectthreshold", BucketConfig.LargeObjectThreshold); if (LargeObjectThreshold == 0) { throw OptionParseException( fmt::format("'largeobjectthreshold' option for cache bucket '{}' is invalid. It must be non-zero.", Name), {}); } BucketConfig.LargeObjectThreshold = LargeObjectThreshold; BucketConfig.LimitOverwrites = Bucket.value().get_or("limitoverwrites", BucketConfig.LimitOverwrites); Value.push_back(std::make_pair(std::move(Name), BucketConfig)); } } } } std::vector>& Value; }; UpstreamCachePolicy ParseUpstreamCachePolicy(std::string_view Options) { if (Options == "readonly") { return UpstreamCachePolicy::Read; } else if (Options == "writeonly") { return UpstreamCachePolicy::Write; } else if (Options == "disabled") { return UpstreamCachePolicy::Disabled; } else { return UpstreamCachePolicy::ReadWrite; } } ZenObjectStoreConfig ParseBucketConfigs(std::span Buckets) { using namespace std::literals; ZenObjectStoreConfig Cfg; // split bucket args in the form of "{BucketName};{LocalPath}" for (std::string_view Bucket : Buckets) { ZenObjectStoreConfig::BucketConfig NewBucket; if (auto Idx = Bucket.find_first_of(";"); Idx != std::string_view::npos) { NewBucket.Name = Bucket.substr(0, Idx); NewBucket.Directory = Bucket.substr(Idx + 1); } else { NewBucket.Name = Bucket; } Cfg.Buckets.push_back(std::move(NewBucket)); } return Cfg; } class CachePolicyOption : public LuaConfig::OptionValue { public: CachePolicyOption(UpstreamCachePolicy& Value) : Value(Value) {} virtual void Print(std::string_view, 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(); 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; }; class ZenAuthConfigOption : public LuaConfig::OptionValue { public: ZenAuthConfigOption(ZenAuthConfig& Value) : Value(Value) {} virtual void Print(std::string_view Indent, StringBuilderBase& StringBuilder) override { if (Value.OpenIdProviders.empty()) { StringBuilder.Append("{}"); return; } LuaConfig::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 OpenIdProviders = Object.as()) { for (const auto& Kv : OpenIdProviders.value()) { if (sol::optional OpenIdProvider = Kv.second.as()) { 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 LuaConfig::OptionValue { public: ZenObjectStoreConfigOption(ZenObjectStoreConfig& Value) : Value(Value) {} virtual void Print(std::string_view Indent, StringBuilderBase& StringBuilder) override { if (Value.Buckets.empty()) { StringBuilder.Append("{}"); return; } LuaConfig::LuaContainerWriter Writer(StringBuilder, Indent); for (const ZenObjectStoreConfig::BucketConfig& Config : Value.Buckets) { Writer.BeginContainer(""); { Writer.WriteValue("name", Config.Name); std::string Directory = Config.Directory.string(); LuaConfig::EscapeBackslash(Directory); Writer.WriteValue("directory", Directory); } Writer.EndContainer(); } } virtual void Parse(sol::object Object) override { if (sol::optional Buckets = Object.as()) { for (const auto& Kv : Buckets.value()) { if (sol::optional Bucket = Kv.second.as()) { 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 = MakeSafeAbsolutePath(Directory)}); } } } } ZenObjectStoreConfig& Value; }; std::shared_ptr MakeOption(UpstreamCachePolicy& Value) { return std::make_shared(Value); }; std::shared_ptr MakeOption(ZenAuthConfig& Value) { return std::make_shared(Value); }; std::shared_ptr MakeOption(ZenObjectStoreConfig& Value) { return std::make_shared(Value); }; std::shared_ptr MakeOption(std::vector>& Value) { return std::make_shared(Value); }; void ZenStorageServerConfigurator::AddConfigOptions(LuaConfig::Options& LuaOptions) { using namespace std::literals; auto& ServerOptions = m_ServerOptions; ////// server LuaOptions.AddOption("server.pluginsconfigfile"sv, ServerOptions.PluginsConfigFile, "plugins-config"sv); ////// objectstore LuaOptions.AddOption("server.objectstore.enabled"sv, ServerOptions.ObjectStoreEnabled, "objectstore-enabled"sv); LuaOptions.AddOption("server.objectstore.buckets"sv, ServerOptions.ObjectStoreConfig); ////// buildsstore LuaOptions.AddOption("server.buildstore.enabled"sv, ServerOptions.BuildStoreConfig.Enabled, "buildstore-enabled"sv); LuaOptions.AddOption("server.buildstore.disksizelimit"sv, ServerOptions.BuildStoreConfig.MaxDiskSpaceLimit, "buildstore-disksizelimit"); ////// cache LuaOptions.AddOption("cache.enable"sv, ServerOptions.StructuredCacheConfig.Enabled); LuaOptions.AddOption("cache.writelog"sv, ServerOptions.StructuredCacheConfig.WriteLogEnabled, "cache-write-log"sv); LuaOptions.AddOption("cache.accesslog"sv, ServerOptions.StructuredCacheConfig.AccessLogEnabled, "cache-access-log"sv); LuaOptions.AddOption("cache.buckets"sv, ServerOptions.StructuredCacheConfig.PerBucketConfigs, "cache.buckets"sv); LuaOptions.AddOption("cache.memlayer.sizethreshold"sv, ServerOptions.StructuredCacheConfig.BucketConfig.MemCacheSizeThreshold, "cache-memlayer-sizethreshold"sv); LuaOptions.AddOption("cache.memlayer.targetfootprint"sv, ServerOptions.StructuredCacheConfig.MemTargetFootprintBytes, "cache-memlayer-targetfootprint"sv); LuaOptions.AddOption("cache.memlayer.triminterval"sv, ServerOptions.StructuredCacheConfig.MemTrimIntervalSeconds, "cache-memlayer-triminterval"sv); LuaOptions.AddOption("cache.memlayer.maxage"sv, ServerOptions.StructuredCacheConfig.MemMaxAgeSeconds, "cache-memlayer-maxage"sv); LuaOptions.AddOption("cache.bucket.maxblocksize"sv, ServerOptions.StructuredCacheConfig.BucketConfig.MaxBlockSize, "cache-bucket-maxblocksize"sv); LuaOptions.AddOption("cache.bucket.memlayer.sizethreshold"sv, ServerOptions.StructuredCacheConfig.BucketConfig.MemCacheSizeThreshold, "cache-bucket-memlayer-sizethreshold"sv); LuaOptions.AddOption("cache.bucket.payloadalignment"sv, ServerOptions.StructuredCacheConfig.BucketConfig.PayloadAlignment, "cache-bucket-payloadalignment"sv); LuaOptions.AddOption("cache.bucket.largeobjectthreshold"sv, ServerOptions.StructuredCacheConfig.BucketConfig.LargeObjectThreshold, "cache-bucket-largeobjectthreshold"sv); LuaOptions.AddOption("cache.bucket.limitoverwrites"sv, ServerOptions.StructuredCacheConfig.BucketConfig.LimitOverwrites, "cache-bucket-limit-overwrites"sv); ////// 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); ////// gc LuaOptions.AddOption("gc.enabled"sv, ServerOptions.GcConfig.Enabled, "gc-enabled"sv); LuaOptions.AddOption("gc.v2"sv, ServerOptions.GcConfig.UseGCV2, "gc-v2"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); LuaOptions.AddOption("gc.lightweightintervalseconds"sv, ServerOptions.GcConfig.LightweightIntervalSeconds, "gc-lightweight-interval-seconds"sv); LuaOptions.AddOption("gc.compactblockthreshold"sv, ServerOptions.GcConfig.CompactBlockUsageThresholdPercent, "gc-compactblock-threshold"sv); LuaOptions.AddOption("gc.verbose"sv, ServerOptions.GcConfig.Verbose, "gc-verbose"sv); LuaOptions.AddOption("gc.single-threaded"sv, ServerOptions.GcConfig.SingleThreaded, "gc-single-threaded"sv); LuaOptions.AddOption("gc.cache.attachment.store"sv, ServerOptions.GcConfig.StoreCacheAttachmentMetaData, "gc-cache-attachment-store"); LuaOptions.AddOption("gc.projectstore.attachment.store"sv, ServerOptions.GcConfig.StoreProjectAttachmentMetaData, "gc-projectstore-attachment-store"); LuaOptions.AddOption("gc.attachment.passes"sv, ServerOptions.GcConfig.AttachmentPassCount, "gc-attachment-passes"sv); LuaOptions.AddOption("gc.validation"sv, ServerOptions.GcConfig.EnableValidation, "gc-validation"); LuaOptions.AddOption("gc.cache.maxdurationseconds"sv, ServerOptions.GcConfig.Cache.MaxDurationSeconds, "gc-cache-duration-seconds"sv); LuaOptions.AddOption("gc.projectstore.duration.seconds"sv, ServerOptions.GcConfig.ProjectStore.MaxDurationSeconds, "gc-projectstore-duration-seconds"); LuaOptions.AddOption("gc.buildstore.duration.seconds"sv, ServerOptions.GcConfig.BuildStore.MaxDurationSeconds, "gc-buildstore-duration-seconds"); ////// 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); ////// workspaces LuaOptions.AddOption("workspaces.enabled"sv, ServerOptions.WorksSpacesConfig.Enabled, "workspaces-enabled"sv); LuaOptions.AddOption("workspaces.allowconfigchanges"sv, ServerOptions.WorksSpacesConfig.AllowConfigurationChanges, "workspaces-allow-changes"sv); } void ZenStorageServerConfigurator::OnConfigFileParsed(LuaConfig::Options& LuaOptions) { using namespace std::literals; // These have special command line processing so we make sure we export them if they were configured on command line if (!m_ServerOptions.AuthConfig.OpenIdProviders.empty()) { LuaOptions.Touch("security.openidproviders"sv); } if (!m_ServerOptions.ObjectStoreConfig.Buckets.empty()) { LuaOptions.Touch("server.objectstore.buckets"sv); } if (!m_ServerOptions.StructuredCacheConfig.PerBucketConfigs.empty()) { LuaOptions.Touch("cache.buckets"sv); } // Also parse plugins config file if (!m_ServerOptions.PluginsConfigFile.empty()) { ParsePluginsConfigFile(m_ServerOptions.PluginsConfigFile); } } void ZenStorageServerConfigurator::AddCliOptions(cxxopts::Options& options) { m_StorageOptions.AddCliOptions(options, m_ServerOptions); } void ZenStorageServerConfigurator::ApplyOptions(cxxopts::Options& options) { m_StorageOptions.ApplyOptions(options, m_ServerOptions); } void ZenStorageServerConfigurator::ParsePluginsConfigFile(const std::filesystem::path& Path) { using namespace std::literals; IoBuffer Body = IoBufferBuilder::MakeFromFile(Path); std::string JsonText(reinterpret_cast(Body.GetData()), Body.GetSize()); std::string JsonError; json11::Json PluginsInfo = json11::Json::parse(JsonText, JsonError); if (!JsonError.empty()) { ZEN_WARN("failed parsing plugins config file '{}'. Reason: '{}'", Path, JsonError); return; } for (const json11::Json& PluginInfo : PluginsInfo.array_items()) { if (!PluginInfo.is_object()) { ZEN_WARN("the json file '{}' does not contain a valid plugin definition, object expected, got '{}'", Path, PluginInfo.dump()); continue; } HttpServerPluginConfig Config = {}; bool bNeedsPort = true; for (const std::pair& Items : PluginInfo.object_items()) { if (!Items.second.is_string()) { ZEN_WARN("the json file '{}' does not contain a valid plugins definition, string expected, got '{}'", Path, Items.second.dump()); continue; } const std::string& Name = Items.first; const std::string& Value = Items.second.string_value(); if (Name == "name"sv) Config.PluginName = Value; else { Config.PluginOptions.push_back({Name, Value}); if (Name == "port"sv) { bNeedsPort = false; } } } // add a default base port in case if json config didn't provide one if (bNeedsPort) { Config.PluginOptions.push_back({"port", std::to_string(m_ServerOptions.BasePort)}); } m_ServerOptions.HttpConfig.PluginConfigs.push_back(Config); } } void ZenStorageServerCmdLineOptions::AddCliOptions(cxxopts::Options& options, ZenStorageServerConfig& ServerOptions) { options.add_options()("plugins-config", "Path to plugins config file", cxxopts::value(PluginsConfigFile)); options.add_options()("scrub", "Validate state at startup", cxxopts::value(ServerOptions.ScrubOptions)->implicit_value("yes"), "(nocas,nogc,nodelete,yes,no)*"); AddSecurityOptions(options, ServerOptions); AddCacheOptions(options, ServerOptions); AddGcOptions(options, ServerOptions); AddObjectStoreOptions(options, ServerOptions); AddBuildStoreOptions(options, ServerOptions); AddWorkspacesOptions(options, ServerOptions); } void ZenStorageServerCmdLineOptions::AddSecurityOptions(cxxopts::Options& options, ZenStorageServerConfig& ServerOptions) { options.add_option("security", "", "encryption-aes-key", "256 bit AES encryption key", cxxopts::value(ServerOptions.EncryptionKey), ""); options.add_option("security", "", "encryption-aes-iv", "128 bit AES encryption initialization vector", cxxopts::value(ServerOptions.EncryptionIV), ""); options.add_option("security", "", "openid-provider-name", "Open ID provider name", cxxopts::value(OpenIdProviderName), "Default"); options.add_option("security", "", "openid-provider-url", "Open ID provider URL", cxxopts::value(OpenIdProviderUrl), ""); options.add_option("security", "", "openid-client-id", "Open ID client ID", cxxopts::value(OpenIdClientId), ""); } void ZenStorageServerCmdLineOptions::AddCacheOptions(cxxopts::Options& options, ZenStorageServerConfig& ServerOptions) { options.add_option("cache", "", "upstream-cache-policy", "", cxxopts::value(UpstreamCachePolicyOptions)->default_value(""), "Upstream cache policy (readwrite|readonly|writeonly|disabled)"); options.add_option("cache", "", "upstream-jupiter-url", "URL to a Jupiter instance", cxxopts::value(ServerOptions.UpstreamCacheConfig.JupiterConfig.Url)->default_value(""), ""); options.add_option("cache", "", "upstream-jupiter-oauth-url", "URL to the OAuth provier", cxxopts::value(ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthUrl)->default_value(""), ""); options.add_option("cache", "", "upstream-jupiter-oauth-clientid", "The OAuth client ID", cxxopts::value(ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthClientId)->default_value(""), ""); options.add_option("cache", "", "upstream-jupiter-oauth-clientsecret", "The OAuth client secret", cxxopts::value(ServerOptions.UpstreamCacheConfig.JupiterConfig.OAuthClientSecret)->default_value(""), ""); options.add_option("cache", "", "upstream-jupiter-openid-provider", "Name of a registered Open ID provider", cxxopts::value(ServerOptions.UpstreamCacheConfig.JupiterConfig.OpenIdProvider)->default_value(""), ""); options.add_option("cache", "", "upstream-jupiter-token", "A static authentication token", cxxopts::value(ServerOptions.UpstreamCacheConfig.JupiterConfig.AccessToken)->default_value(""), ""); options.add_option("cache", "", "upstream-jupiter-namespace", "The Common Blob Store API namespace", cxxopts::value(ServerOptions.UpstreamCacheConfig.JupiterConfig.Namespace)->default_value(""), ""); options.add_option("cache", "", "upstream-jupiter-namespace-ddc", "The lecacy DDC namespace", cxxopts::value(ServerOptions.UpstreamCacheConfig.JupiterConfig.DdcNamespace)->default_value(""), ""); options.add_option("cache", "", "upstream-zen-url", "URL to remote Zen server. Use a comma separated list to choose the one with the best latency.", cxxopts::value>(ServerOptions.UpstreamCacheConfig.ZenConfig.Urls), ""); options.add_option("cache", "", "upstream-zen-dns", "DNS that resolves to one or more Zen server instance(s)", cxxopts::value>(ServerOptions.UpstreamCacheConfig.ZenConfig.Dns), ""); options.add_option("cache", "", "upstream-thread-count", "Number of threads used for upstream procsssing", cxxopts::value(ServerOptions.UpstreamCacheConfig.UpstreamThreadCount)->default_value("4"), ""); options.add_option("cache", "", "upstream-connect-timeout-ms", "Connect timeout in millisecond(s). Default 5000 ms.", cxxopts::value(ServerOptions.UpstreamCacheConfig.ConnectTimeoutMilliseconds)->default_value("5000"), ""); options.add_option("cache", "", "upstream-timeout-ms", "Timeout in millisecond(s). Default 0 ms", cxxopts::value(ServerOptions.UpstreamCacheConfig.TimeoutMilliseconds)->default_value("0"), ""); options.add_option("cache", "", "cache-write-log", "Whether cache write log is enabled", cxxopts::value(ServerOptions.StructuredCacheConfig.WriteLogEnabled)->default_value("false"), ""); options.add_option("cache", "", "cache-access-log", "Whether cache access log is enabled", cxxopts::value(ServerOptions.StructuredCacheConfig.AccessLogEnabled)->default_value("false"), ""); options.add_option( "cache", "", "cache-memlayer-sizethreshold", "The largest size of a cache entry that may be cached in memory. Default set to 1024 (1 Kb). Set to 0 to disable memory " "caching. " "Obsolete, replaced by `--cache-bucket-memlayer-sizethreshold`", cxxopts::value(ServerOptions.StructuredCacheConfig.BucketConfig.MemCacheSizeThreshold)->default_value("1024"), ""); options.add_option("cache", "", "cache-memlayer-targetfootprint", "Max allowed memory used by cache memory layer per namespace in bytes. Default set to 536870912 (512 Mb).", cxxopts::value(ServerOptions.StructuredCacheConfig.MemTargetFootprintBytes)->default_value("536870912"), ""); options.add_option("cache", "", "cache-memlayer-triminterval", "Minimum time between each attempt to trim cache memory layers in seconds. Default set to 60 (1 min).", cxxopts::value(ServerOptions.StructuredCacheConfig.MemTrimIntervalSeconds)->default_value("60"), ""); options.add_option("cache", "", "cache-memlayer-maxage", "Maximum age of payloads when trimming cache memory layers in seconds. Default set to 86400 (1 day).", cxxopts::value(ServerOptions.StructuredCacheConfig.MemMaxAgeSeconds)->default_value("86400"), ""); options.add_option("compute", "", "lie-cpus", "Lie to upstream about CPU capabilities", cxxopts::value(ServerOptions.LieCpu), ""); options.add_option("cache", "", "cache-bucket-maxblocksize", "Max size of cache bucket blocks. Default set to 1073741824 (1GB).", cxxopts::value(ServerOptions.StructuredCacheConfig.BucketConfig.MaxBlockSize)->default_value("1073741824"), ""); options.add_option("cache", "", "cache-bucket-payloadalignment", "Payload alignement for cache bucket blocks. Default set to 16.", cxxopts::value(ServerOptions.StructuredCacheConfig.BucketConfig.PayloadAlignment)->default_value("16"), ""); options.add_option( "cache", "", "cache-bucket-largeobjectthreshold", "Threshold for storing cache bucket values as loose files. Default set to 131072 (128 KB).", cxxopts::value(ServerOptions.StructuredCacheConfig.BucketConfig.LargeObjectThreshold)->default_value("131072"), ""); options.add_option( "cache", "", "cache-bucket-memlayer-sizethreshold", "The largest size of a cache entry that may be cached in memory. Default set to 1024 (1 Kb). Set to 0 to disable memory " "caching.", cxxopts::value(ServerOptions.StructuredCacheConfig.BucketConfig.MemCacheSizeThreshold)->default_value("1024"), ""); options.add_option("cache", "", "cache-bucket-limit-overwrites", "Whether to require policy flag pattern before allowing overwrites in cache bucket", cxxopts::value(ServerOptions.StructuredCacheConfig.BucketConfig.LimitOverwrites)->default_value("true"), ""); } void ZenStorageServerCmdLineOptions::AddGcOptions(cxxopts::Options& options, ZenStorageServerConfig& ServerOptions) { options.add_option("gc", "", "gc-cache-attachment-store", "Enable storing attachments referenced by a cache record in block store meta data.", cxxopts::value(ServerOptions.GcConfig.StoreCacheAttachmentMetaData)->default_value("false"), ""); options.add_option("gc", "", "gc-projectstore-attachment-store", "Enable storing attachments referenced by project oplogs in meta data.", cxxopts::value(ServerOptions.GcConfig.StoreProjectAttachmentMetaData)->default_value("false"), ""); options.add_option("gc", "", "gc-validation", "Enable validation of references after full GC.", cxxopts::value(ServerOptions.GcConfig.EnableValidation)->default_value("true"), ""); options.add_option("gc", "", "gc-enabled", "Whether garbage collection is enabled or not.", cxxopts::value(ServerOptions.GcConfig.Enabled)->default_value("true"), ""); options.add_option("gc", "", "gc-v2", "Use V2 of GC implementation or not.", cxxopts::value(ServerOptions.GcConfig.UseGCV2)->default_value("true"), ""); options.add_option("gc", "", "gc-small-objects", "Whether garbage collection of small objects is enabled or not.", cxxopts::value(ServerOptions.GcConfig.CollectSmallObjects)->default_value("true"), ""); options.add_option("gc", "", "gc-interval-seconds", "Garbage collection interval in seconds. Default set to 3600 (1 hour).", cxxopts::value(ServerOptions.GcConfig.IntervalSeconds)->default_value("3600"), ""); options.add_option("gc", "", "gc-lightweight-interval-seconds", "Lightweight garbage collection interval in seconds. Default set to 900 (30 min).", cxxopts::value(ServerOptions.GcConfig.LightweightIntervalSeconds)->default_value("900"), ""); options.add_option("gc", "", "gc-cache-duration-seconds", "Max duration in seconds before Z$ entries get evicted. Default set to 1209600 (2 weeks)", cxxopts::value(ServerOptions.GcConfig.Cache.MaxDurationSeconds)->default_value("1209600"), ""); options.add_option("gc", "", "gc-projectstore-duration-seconds", "Max duration in seconds before project store entries get evicted. Default set to 1209600 (2 weeks)", cxxopts::value(ServerOptions.GcConfig.ProjectStore.MaxDurationSeconds)->default_value("1209600"), ""); options.add_option("gc", "", "gc-buildstore-duration-seconds", "Max duration in seconds before build store entries get evicted. Default set to 604800 (1 week)", cxxopts::value(ServerOptions.GcConfig.BuildStore.MaxDurationSeconds)->default_value("604800"), ""); options.add_option("gc", "", "disk-reserve-size", "Size of gc disk reserve in bytes. Default set to 268435456 (256 Mb). Set to zero to disable.", cxxopts::value(ServerOptions.GcConfig.DiskReserveSize)->default_value("268435456"), ""); options.add_option("gc", "", "gc-monitor-interval-seconds", "Garbage collection monitoring interval in seconds. Default set to 30 (30 seconds)", cxxopts::value(ServerOptions.GcConfig.MonitorIntervalSeconds)->default_value("30"), ""); options.add_option("gc", "", "gc-low-diskspace-threshold", "Minimum free space on disk to allow writes to disk. Default set to 268435456 (256 Mb). Set to zero to disable.", cxxopts::value(ServerOptions.GcConfig.MinimumFreeDiskSpaceToAllowWrites)->default_value("268435456"), ""); options.add_option("gc", "", "gc-disksize-softlimit", "Garbage collection disk usage soft limit. Default set to 0 (Off).", cxxopts::value(ServerOptions.GcConfig.DiskSizeSoftLimit)->default_value("0"), ""); options.add_option("gc", "", "gc-compactblock-threshold", "Garbage collection - how much of a compact block should be used to skip compacting the block. 0 - compact only " "empty eligible blocks, 100 - compact all non-full eligible blocks.", cxxopts::value(ServerOptions.GcConfig.CompactBlockUsageThresholdPercent)->default_value("60"), ""); options.add_option("gc", "", "gc-verbose", "Enable verbose logging for GC.", cxxopts::value(ServerOptions.GcConfig.Verbose)->default_value("false"), ""); options.add_option("gc", "", "gc-single-threaded", "Force GC to run single threaded.", cxxopts::value(ServerOptions.GcConfig.SingleThreaded)->default_value("false"), ""); options.add_option("gc", "", "gc-attachment-passes", "Limit the range of unreferenced attachments included in GC check by breaking it into passes. Default is one pass " "which includes all the attachments.", cxxopts::value(ServerOptions.GcConfig.AttachmentPassCount)->default_value("1"), ""); } void ZenStorageServerCmdLineOptions::AddObjectStoreOptions(cxxopts::Options& options, ZenStorageServerConfig& ServerOptions) { options.add_option("objectstore", "", "objectstore-enabled", "Whether the object store is enabled or not.", cxxopts::value(ServerOptions.ObjectStoreEnabled)->default_value("false"), ""); options.add_option("objectstore", "", "objectstore-bucket", "Object store bucket mappings.", cxxopts::value>(BucketConfigs), ""); } void ZenStorageServerCmdLineOptions::AddBuildStoreOptions(cxxopts::Options& options, ZenStorageServerConfig& ServerOptions) { options.add_option("buildstore", "", "buildstore-enabled", "Whether the builds store is enabled or not.", cxxopts::value(ServerOptions.BuildStoreConfig.Enabled)->default_value("false"), ""); options.add_option("buildstore", "", "buildstore-disksizelimit", "Max number of bytes before build store entries get evicted. Default set to 1099511627776 (1TB week)", cxxopts::value(ServerOptions.BuildStoreConfig.MaxDiskSpaceLimit)->default_value("1099511627776"), ""); } void ZenStorageServerCmdLineOptions::AddWorkspacesOptions(cxxopts::Options& options, ZenStorageServerConfig& ServerOptions) { options.add_option("workspaces", "", "workspaces-enabled", "", cxxopts::value(ServerOptions.WorksSpacesConfig.Enabled)->default_value("true"), "Enable workspaces support with folder sharing"); options.add_option("workspaces", "", "workspaces-allow-changes", "", cxxopts::value(ServerOptions.WorksSpacesConfig.AllowConfigurationChanges)->default_value("false"), "Allow adding/modifying/deleting of workspace and shares via http endpoint"); } void ZenStorageServerCmdLineOptions::ApplyOptions(cxxopts::Options& options, ZenStorageServerConfig& ServerOptions) { ServerOptions.PluginsConfigFile = MakeSafeAbsolutePath(PluginsConfigFile); ServerOptions.UpstreamCacheConfig.CachePolicy = ParseUpstreamCachePolicy(UpstreamCachePolicyOptions); if (OpenIdProviderUrl.empty() == false) { if (OpenIdClientId.empty()) { throw OptionParseException("'--openid-provider-url' requires '--openid-client-id'", options.help()); } ServerOptions.AuthConfig.OpenIdProviders.push_back( {.Name = OpenIdProviderName, .Url = OpenIdProviderUrl, .ClientId = OpenIdClientId}); } ServerOptions.ObjectStoreConfig = ParseBucketConfigs(BucketConfigs); } } // namespace zen