Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New: added environment variable support for all options in config.xml #6746

Merged
merged 2 commits into from
Apr 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/NzbDrone.Common.Test/ConfigFileProviderTest.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System.Collections.Generic;
using FluentAssertions;
using Microsoft.Extensions.Options;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Options;
using NzbDrone.Core.Authentication;
using NzbDrone.Core.Configuration;
using NzbDrone.Test.Common;
Expand Down Expand Up @@ -43,6 +45,26 @@ protected void WithMockConfigFile(string configFile)
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.WriteAllText(configFile, It.IsAny<string>()))
.Callback<string, string>((p, t) => _configFileContents = t);

Mocker.GetMock<IOptions<AuthOptions>>()
.Setup(v => v.Value)
.Returns(new AuthOptions());

Mocker.GetMock<IOptions<AppOptions>>()
.Setup(v => v.Value)
.Returns(new AppOptions());

Mocker.GetMock<IOptions<ServerOptions>>()
.Setup(v => v.Value)
.Returns(new ServerOptions());

Mocker.GetMock<IOptions<LogOptions>>()
.Setup(v => v.Value)
.Returns(new LogOptions());

Mocker.GetMock<IOptions<UpdateOptions>>()
.Setup(v => v.Value)
.Returns(new UpdateOptions());
}

[Test]
Expand Down
6 changes: 6 additions & 0 deletions src/NzbDrone.Common.Test/ServiceFactoryFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using NzbDrone.Common.Composition.Extensions;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Common.Options;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Extensions;
using NzbDrone.Core.Lifecycle;
Expand All @@ -33,6 +34,11 @@ public void event_handlers_should_be_unique()

container.RegisterInstance(new Mock<IHostLifetime>().Object);
container.RegisterInstance(new Mock<IOptions<PostgresOptions>>().Object);
container.RegisterInstance(new Mock<IOptions<AppOptions>>().Object);
container.RegisterInstance(new Mock<IOptions<AuthOptions>>().Object);
container.RegisterInstance(new Mock<IOptions<ServerOptions>>().Object);
container.RegisterInstance(new Mock<IOptions<LogOptions>>().Object);
container.RegisterInstance(new Mock<IOptions<UpdateOptions>>().Object);

var serviceProvider = container.GetServiceProvider();

Expand Down
8 changes: 8 additions & 0 deletions src/NzbDrone.Common/Options/AppOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace NzbDrone.Common.Options;

public class AppOptions
{
public string InstanceName { get; set; }
public string Theme { get; set; }
public bool? LaunchBrowser { get; set; }
}
9 changes: 9 additions & 0 deletions src/NzbDrone.Common/Options/AuthOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace NzbDrone.Common.Options;

public class AuthOptions
{
public string ApiKey { get; set; }
public bool? Enabled { get; set; }
public string Method { get; set; }
public string Required { get; set; }
}
14 changes: 14 additions & 0 deletions src/NzbDrone.Common/Options/LogOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace NzbDrone.Common.Options;

public class LogOptions
{
public string Level { get; set; }
public bool? FilterSentryEvents { get; set; }
public int? Rotate { get; set; }
public bool? Sql { get; set; }
public string ConsoleLevel { get; set; }
public bool? AnalyticsEnabled { get; set; }
public string SyslogServer { get; set; }
public int? SyslogPort { get; set; }
public string SyslogLevel { get; set; }
}
12 changes: 12 additions & 0 deletions src/NzbDrone.Common/Options/ServerOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace NzbDrone.Common.Options;

public class ServerOptions
{
public string UrlBase { get; set; }
public string BindAddress { get; set; }
public int? Port { get; set; }
public bool? EnableSsl { get; set; }
public int? SslPort { get; set; }
public string SslCertPath { get; set; }
public string SslCertPassword { get; set; }
}
9 changes: 9 additions & 0 deletions src/NzbDrone.Common/Options/UpdateOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace NzbDrone.Common.Options;

public class UpdateOptions
{
public string Mechanism { get; set; }
public bool? Automatically { get; set; }
public string ScriptPath { get; set; }
public string Branch { get; set; }
}
80 changes: 52 additions & 28 deletions src/NzbDrone.Core/Configuration/ConfigFileProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Options;
using NzbDrone.Core.Authentication;
using NzbDrone.Core.Configuration.Events;
using NzbDrone.Core.Datastore;
Expand Down Expand Up @@ -70,6 +71,11 @@ public class ConfigFileProvider : IConfigFileProvider
private readonly IDiskProvider _diskProvider;
private readonly ICached<string> _cache;
private readonly PostgresOptions _postgresOptions;
private readonly AuthOptions _authOptions;
private readonly AppOptions _appOptions;
private readonly ServerOptions _serverOptions;
private readonly UpdateOptions _updateOptions;
private readonly LogOptions _logOptions;

private readonly string _configFile;
private static readonly Regex HiddenCharacterRegex = new Regex("[^a-z0-9]", RegexOptions.Compiled | RegexOptions.IgnoreCase);
Expand All @@ -80,13 +86,23 @@ public class ConfigFileProvider : IConfigFileProvider
ICacheManager cacheManager,
IEventAggregator eventAggregator,
IDiskProvider diskProvider,
IOptions<PostgresOptions> postgresOptions)
IOptions<PostgresOptions> postgresOptions,
IOptions<AuthOptions> authOptions,
IOptions<AppOptions> appOptions,
IOptions<ServerOptions> serverOptions,
IOptions<UpdateOptions> updateOptions,
IOptions<LogOptions> logOptions)
{
_cache = cacheManager.GetCache<string>(GetType());
_eventAggregator = eventAggregator;
_diskProvider = diskProvider;
_configFile = appFolderInfo.GetConfigPath();
_postgresOptions = postgresOptions.Value;
_authOptions = authOptions.Value;
_appOptions = appOptions.Value;
_serverOptions = serverOptions.Value;
_updateOptions = updateOptions.Value;
_logOptions = logOptions.Value;
}

public Dictionary<string, object> GetConfigDictionary()
Expand Down Expand Up @@ -142,7 +158,7 @@ public string BindAddress
{
const string defaultValue = "*";

var bindAddress = GetValue("BindAddress", defaultValue);
var bindAddress = _serverOptions.BindAddress ?? GetValue("BindAddress", defaultValue);
if (string.IsNullOrWhiteSpace(bindAddress))
{
return defaultValue;
Expand All @@ -152,19 +168,19 @@ public string BindAddress
}
}

public int Port => GetValueInt("Port", 8989);
public int Port => _serverOptions.Port ?? GetValueInt("Port", 8989);

public int SslPort => GetValueInt("SslPort", 9898);
public int SslPort => _serverOptions.SslPort ?? GetValueInt("SslPort", 9898);

public bool EnableSsl => GetValueBoolean("EnableSsl", false);
public bool EnableSsl => _serverOptions.EnableSsl ?? GetValueBoolean("EnableSsl", false);

public bool LaunchBrowser => GetValueBoolean("LaunchBrowser", true);
public bool LaunchBrowser => _appOptions.LaunchBrowser ?? GetValueBoolean("LaunchBrowser", true);

public string ApiKey
{
get
{
var apiKey = GetValue("ApiKey", GenerateApiKey());
var apiKey = _authOptions.ApiKey ?? GetValue("ApiKey", GenerateApiKey());

if (apiKey.IsNullOrWhiteSpace())
{
Expand All @@ -180,28 +196,33 @@ public AuthenticationType AuthenticationMethod
{
get
{
var enabled = GetValueBoolean("AuthenticationEnabled", false, false);
var enabled = _authOptions.Enabled ?? GetValueBoolean("AuthenticationEnabled", false, false);

if (enabled)
{
SetValue("AuthenticationMethod", AuthenticationType.Basic);
return AuthenticationType.Basic;
}

return GetValueEnum("AuthenticationMethod", AuthenticationType.None);
return Enum.TryParse<AuthenticationType>(_authOptions.Method, out var enumValue)
? enumValue
: GetValueEnum("AuthenticationMethod", AuthenticationType.None);
}
}

public AuthenticationRequiredType AuthenticationRequired => GetValueEnum("AuthenticationRequired", AuthenticationRequiredType.Enabled);
public AuthenticationRequiredType AuthenticationRequired =>
Enum.TryParse<AuthenticationRequiredType>(_authOptions.Required, out var enumValue)
? enumValue
: GetValueEnum("AuthenticationRequired", AuthenticationRequiredType.Enabled);

public bool AnalyticsEnabled => GetValueBoolean("AnalyticsEnabled", true, persist: false);
public bool AnalyticsEnabled => _logOptions.AnalyticsEnabled ?? GetValueBoolean("AnalyticsEnabled", true, persist: false);

public string Branch => GetValue("Branch", "main").ToLowerInvariant();
public string Branch => _updateOptions.Branch ?? GetValue("Branch", "main").ToLowerInvariant();

public string LogLevel => GetValue("LogLevel", "info").ToLowerInvariant();
public string ConsoleLogLevel => GetValue("ConsoleLogLevel", string.Empty, persist: false);
public string LogLevel => _logOptions.Level ?? GetValue("LogLevel", "info").ToLowerInvariant();
public string ConsoleLogLevel => _logOptions.ConsoleLevel ?? GetValue("ConsoleLogLevel", string.Empty, persist: false);

public string Theme => GetValue("Theme", "auto", persist: false);
public string Theme => _appOptions.Theme ?? GetValue("Theme", "auto", persist: false);

public string PostgresHost => _postgresOptions?.Host ?? GetValue("PostgresHost", string.Empty, persist: false);
public string PostgresUser => _postgresOptions?.User ?? GetValue("PostgresUser", string.Empty, persist: false);
Expand All @@ -210,17 +231,17 @@ public AuthenticationType AuthenticationMethod
public string PostgresLogDb => _postgresOptions?.LogDb ?? GetValue("PostgresLogDb", "sonarr-log", persist: false);
public int PostgresPort => (_postgresOptions?.Port ?? 0) != 0 ? _postgresOptions.Port : GetValueInt("PostgresPort", 5432, persist: false);

public bool LogSql => GetValueBoolean("LogSql", false, persist: false);
public int LogRotate => GetValueInt("LogRotate", 50, persist: false);
public bool FilterSentryEvents => GetValueBoolean("FilterSentryEvents", true, persist: false);
public string SslCertPath => GetValue("SslCertPath", "");
public string SslCertPassword => GetValue("SslCertPassword", "");
public bool LogSql => _logOptions.Sql ?? GetValueBoolean("LogSql", false, persist: false);
public int LogRotate => _logOptions.Rotate ?? GetValueInt("LogRotate", 50, persist: false);
public bool FilterSentryEvents => _logOptions.FilterSentryEvents ?? GetValueBoolean("FilterSentryEvents", true, persist: false);
public string SslCertPath => _serverOptions.SslCertPath ?? GetValue("SslCertPath", "");
public string SslCertPassword => _serverOptions.SslCertPassword ?? GetValue("SslCertPassword", "");

public string UrlBase
{
get
{
var urlBase = GetValue("UrlBase", "").Trim('/');
var urlBase = _serverOptions.UrlBase ?? GetValue("UrlBase", "").Trim('/');

if (urlBase.IsNullOrWhiteSpace())
{
Expand All @@ -237,7 +258,7 @@ public string InstanceName
{
get
{
var instanceName = GetValue("InstanceName", BuildInfo.AppName);
var instanceName = _appOptions.InstanceName ?? GetValue("InstanceName", BuildInfo.AppName);

if (instanceName.StartsWith(BuildInfo.AppName) || instanceName.EndsWith(BuildInfo.AppName))
{
Expand All @@ -248,17 +269,20 @@ public string InstanceName
}
}

public bool UpdateAutomatically => GetValueBoolean("UpdateAutomatically", false, false);
public bool UpdateAutomatically => _updateOptions.Automatically ?? GetValueBoolean("UpdateAutomatically", false, false);

public UpdateMechanism UpdateMechanism => GetValueEnum("UpdateMechanism", UpdateMechanism.BuiltIn, false);
public UpdateMechanism UpdateMechanism =>
Enum.TryParse<UpdateMechanism>(_updateOptions.Mechanism, out var enumValue)
? enumValue
: GetValueEnum("UpdateMechanism", UpdateMechanism.BuiltIn, false);

public string UpdateScriptPath => GetValue("UpdateScriptPath", "", false);
public string UpdateScriptPath => _updateOptions.ScriptPath ?? GetValue("UpdateScriptPath", "", false);

public string SyslogServer => GetValue("SyslogServer", "", persist: false);
public string SyslogServer => _logOptions.SyslogServer ?? GetValue("SyslogServer", "", persist: false);

public int SyslogPort => GetValueInt("SyslogPort", 514, persist: false);
public int SyslogPort => _logOptions.SyslogPort ?? GetValueInt("SyslogPort", 514, persist: false);

public string SyslogLevel => GetValue("SyslogLevel", LogLevel, persist: false).ToLowerInvariant();
public string SyslogLevel => _logOptions.SyslogLevel ?? GetValue("SyslogLevel", LogLevel, persist: false).ToLowerInvariant();

public int GetValueInt(string key, int defaultValue, bool persist = true)
{
Expand Down
6 changes: 6 additions & 0 deletions src/NzbDrone.Host.Test/ContainerFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using NzbDrone.Common.Composition.Extensions;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Common.Options;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Extensions;
using NzbDrone.Core.Download;
Expand Down Expand Up @@ -46,6 +47,11 @@ public void SetUp()
container.RegisterInstance<IHostLifetime>(new Mock<IHostLifetime>().Object);
container.RegisterInstance<IBroadcastSignalRMessage>(new Mock<IBroadcastSignalRMessage>().Object);
container.RegisterInstance<IOptions<PostgresOptions>>(new Mock<IOptions<PostgresOptions>>().Object);
container.RegisterInstance<IOptions<AuthOptions>>(new Mock<IOptions<AuthOptions>>().Object);
container.RegisterInstance<IOptions<AppOptions>>(new Mock<IOptions<AppOptions>>().Object);
container.RegisterInstance<IOptions<ServerOptions>>(new Mock<IOptions<ServerOptions>>().Object);
container.RegisterInstance<IOptions<UpdateOptions>>(new Mock<IOptions<UpdateOptions>>().Object);
container.RegisterInstance<IOptions<LogOptions>>(new Mock<IOptions<LogOptions>>().Object);

_container = container.GetServiceProvider();
}
Expand Down
23 changes: 17 additions & 6 deletions src/NzbDrone.Host/Bootstrap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Common.Options;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Datastore.Extensions;
using Sonarr.Http.ClientSchema;
Expand Down Expand Up @@ -98,6 +99,11 @@ public static void Start(string[] args, Action<IHostBuilder> trayCallback = null
.ConfigureServices(services =>
{
services.Configure<PostgresOptions>(config.GetSection("Sonarr:Postgres"));
services.Configure<AppOptions>(config.GetSection("Sonarr:App"));
services.Configure<AuthOptions>(config.GetSection("Sonarr:Auth"));
services.Configure<ServerOptions>(config.GetSection("Sonarr:Server"));
services.Configure<LogOptions>(config.GetSection("Sonarr:Log"));
services.Configure<UpdateOptions>(config.GetSection("Sonarr:Update"));
}).Build();

break;
Expand All @@ -119,12 +125,12 @@ public static IHostBuilder CreateConsoleHostBuilder(string[] args, StartupContex
{
var config = GetConfiguration(context);

var bindAddress = config.GetValue(nameof(ConfigFileProvider.BindAddress), "*");
var port = config.GetValue(nameof(ConfigFileProvider.Port), 8989);
var sslPort = config.GetValue(nameof(ConfigFileProvider.SslPort), 9898);
var enableSsl = config.GetValue(nameof(ConfigFileProvider.EnableSsl), false);
var sslCertPath = config.GetValue<string>(nameof(ConfigFileProvider.SslCertPath));
var sslCertPassword = config.GetValue<string>(nameof(ConfigFileProvider.SslCertPassword));
var bindAddress = config.GetValue<string>($"Sonarr:Server:{nameof(ServerOptions.BindAddress)}") ?? config.GetValue(nameof(ConfigFileProvider.BindAddress), "*");
var port = config.GetValue<int?>($"Sonarr:Server:{nameof(ServerOptions.Port)}") ?? config.GetValue(nameof(ConfigFileProvider.Port), 8989);
var sslPort = config.GetValue<int?>($"Sonarr:Server:{nameof(ServerOptions.SslPort)}") ?? config.GetValue(nameof(ConfigFileProvider.SslPort), 9898);
var enableSsl = config.GetValue<bool?>($"Sonarr:Server:{nameof(ServerOptions.EnableSsl)}") ?? config.GetValue(nameof(ConfigFileProvider.EnableSsl), false);
var sslCertPath = config.GetValue<string>($"Sonarr:Server:{nameof(ServerOptions.SslCertPath)}") ?? config.GetValue<string>(nameof(ConfigFileProvider.SslCertPath));
var sslCertPassword = config.GetValue<string>($"Sonarr:Server:{nameof(ServerOptions.SslCertPassword)}") ?? config.GetValue<string>(nameof(ConfigFileProvider.SslCertPassword));

var urls = new List<string> { BuildUrl("http", bindAddress, port) };

Expand Down Expand Up @@ -152,6 +158,11 @@ public static IHostBuilder CreateConsoleHostBuilder(string[] args, StartupContex
.ConfigureServices(services =>
{
services.Configure<PostgresOptions>(config.GetSection("Sonarr:Postgres"));
services.Configure<AppOptions>(config.GetSection("Sonarr:App"));
services.Configure<AuthOptions>(config.GetSection("Sonarr:Auth"));
services.Configure<ServerOptions>(config.GetSection("Sonarr:Server"));
services.Configure<LogOptions>(config.GetSection("Sonarr:Log"));
services.Configure<UpdateOptions>(config.GetSection("Sonarr:Update"));
})
.ConfigureWebHost(builder =>
{
Expand Down
Loading