diff --git a/src/NzbDrone.Core/Configuration/DeploymentInfoProvider.cs b/src/NzbDrone.Core/Configuration/DeploymentInfoProvider.cs new file mode 100644 index 000000000..f2609ab66 --- /dev/null +++ b/src/NzbDrone.Core/Configuration/DeploymentInfoProvider.cs @@ -0,0 +1,118 @@ +using NzbDrone.Common.Disk; +using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Update; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace NzbDrone.Core.Configuration +{ + public interface IDeploymentInfoProvider + { + Version PackageVersion { get; } + string PackageBranch { get; } + UpdateMechanism PackageUpdateMechanism { get; } + + Version ReleaseVersion { get; } + string ReleaseBranch { get; } + + bool BuiltInUpdaterAllowed { get; } + UpdateMechanism DefaultUpdateMechanism { get; } + string DefaultBranch { get; } + } + + public class DeploymentInfoProvider : IDeploymentInfoProvider + { + public DeploymentInfoProvider(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider) + { + var bin = appFolderInfo.StartUpFolder; + var packageInfoPath = Path.Combine(bin, "..", "package_info"); + var releaseInfoPath = Path.Combine(bin, "release_info"); + + PackageUpdateMechanism = UpdateMechanism.BuiltIn; + DefaultBranch = "master"; + + if (Path.GetFileName(bin) == "bin" && diskProvider.FileExists(packageInfoPath)) + { + var data = diskProvider.ReadAllText(packageInfoPath); + + PackageVersion = ReadVersion(data, "PackageVersion"); + PackageUpdateMechanism = ReadEnumValue(data, "UpdateMethod", UpdateMechanism.BuiltIn); + PackageBranch = ReadValue(data, "Branch", null); + + ReleaseVersion = ReadVersion(data, "ReleaseVersion"); + + if (PackageBranch.IsNotNullOrWhiteSpace()) + { + DefaultBranch = PackageBranch; + } + } + + if (diskProvider.FileExists(releaseInfoPath)) + { + var data = diskProvider.ReadAllText(releaseInfoPath); + + ReleaseVersion = ReadVersion(data, "ReleaseVersion", ReleaseVersion); + ReleaseBranch = ReadValue(data, "Branch", null); + + if (ReleaseBranch.IsNotNullOrWhiteSpace()) + { + DefaultBranch = ReleaseBranch; + } + } + + DefaultUpdateMechanism = PackageUpdateMechanism; + } + + private static string ReadValue(string fileData, string key, string defaultValue) + { + var match = Regex.Match(fileData, "^" + key + "=(.*)$", RegexOptions.Multiline); + if (match.Success) + { + return match.Groups[1].Value.Trim(); + } + + return defaultValue; + } + + private static T ReadEnumValue(string fileData, string key, T defaultValue) + where T : struct + { + var value = ReadValue(fileData, key, null); + if (value != null && Enum.TryParse(value, true, out var result)) + { + return result; + } + + return defaultValue; + } + + private static Version ReadVersion(string fileData, string key, Version defaultValue = null) + { + var value = ReadValue(fileData, key, null); + if (value != null && Version.TryParse(value, out var result)) + { + return result; + } + + return defaultValue; + } + + public Version PackageVersion { get; private set; } + public string PackageBranch { get; private set; } + public UpdateMechanism PackageUpdateMechanism { get; private set; } + + public Version ReleaseVersion { get; set; } + public string ReleaseBranch { get; set; } + + + public bool BuiltInUpdaterAllowed => PackageUpdateMechanism == UpdateMechanism.BuiltIn; + public UpdateMechanism DefaultUpdateMechanism { get; private set; } + public string DefaultBranch { get; private set; } + } +} diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index dbc5a5dd0..b4243c611 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -129,6 +129,7 @@ + @@ -1225,6 +1226,7 @@ + diff --git a/src/NzbDrone.Core/Update/ConfigureUpdateMechanism.cs b/src/NzbDrone.Core/Update/ConfigureUpdateMechanism.cs new file mode 100644 index 000000000..eb4f4998b --- /dev/null +++ b/src/NzbDrone.Core/Update/ConfigureUpdateMechanism.cs @@ -0,0 +1,62 @@ +using NLog; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Messaging.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NzbDrone.Core.Update +{ + public interface IUpdaterConfigProvider + { + + } + + public class UpdaterConfigProvider : IUpdaterConfigProvider, IHandle + { + private Logger _logger; + private IConfigFileProvider _configFileProvider; + private IDeploymentInfoProvider _deploymentInfoProvider; + + public UpdaterConfigProvider(IDeploymentInfoProvider deploymentInfoProvider, IConfigFileProvider configFileProvider, Logger logger) + { + _deploymentInfoProvider = deploymentInfoProvider; + _configFileProvider = configFileProvider; + _logger = logger; + } + + public void Handle(ApplicationStartedEvent message) + { + var updateMechanism = _configFileProvider.UpdateMechanism; + var packageUpdateMechanism = _deploymentInfoProvider.PackageUpdateMechanism; + + var externalMechanisms = Enum.GetValues(typeof(UpdateMechanism)) + .Cast() + .Where(v => (int)v >= (int)UpdateMechanism.External) + .ToArray(); + + foreach (var externalMechanism in externalMechanisms) + { + if (packageUpdateMechanism != externalMechanism && updateMechanism == externalMechanism || + packageUpdateMechanism == externalMechanism && updateMechanism == UpdateMechanism.BuiltIn) + { + _logger.Info("Update mechanism {0} not supported in the current configuration, changing to {1}.", updateMechanism, packageUpdateMechanism); + ChangeUpdateMechanism(packageUpdateMechanism); + break; + } + } + } + + private void ChangeUpdateMechanism(UpdateMechanism updateMechanism) + { + var config = new Dictionary + { + [nameof(_configFileProvider.UpdateMechanism)] = updateMechanism + }; + _configFileProvider.SaveConfigDictionary(config); + } + } +} diff --git a/src/NzbDrone.Core/Update/InstallUpdateService.cs b/src/NzbDrone.Core/Update/InstallUpdateService.cs index d99bfb305..c38cfc453 100644 --- a/src/NzbDrone.Core/Update/InstallUpdateService.cs +++ b/src/NzbDrone.Core/Update/InstallUpdateService.cs @@ -29,6 +29,7 @@ namespace NzbDrone.Core.Update private readonly IProcessProvider _processProvider; private readonly IVerifyUpdates _updateVerifier; private readonly IStartupContext _startupContext; + private readonly IDeploymentInfoProvider _deploymentInfoProvider; private readonly IConfigFileProvider _configFileProvider; private readonly IRuntimeInfo _runtimeInfo; private readonly IBackupService _backupService; @@ -43,6 +44,7 @@ namespace NzbDrone.Core.Update IProcessProvider processProvider, IVerifyUpdates updateVerifier, IStartupContext startupContext, + IDeploymentInfoProvider deploymentInfoProvider, IConfigFileProvider configFileProvider, IRuntimeInfo runtimeInfo, IBackupService backupService, @@ -61,6 +63,7 @@ namespace NzbDrone.Core.Update _processProvider = processProvider; _updateVerifier = updateVerifier; _startupContext = startupContext; + _deploymentInfoProvider = deploymentInfoProvider; _configFileProvider = configFileProvider; _runtimeInfo = runtimeInfo; _backupService = backupService; @@ -87,6 +90,12 @@ namespace NzbDrone.Core.Update } } + if (_appFolderInfo.StartUpFolder.EndsWith("_output")) + { + _logger.ProgressDebug("Running in developer environment, not updating."); + return; + } + var updateSandboxFolder = _appFolderInfo.GetUpdateSandboxFolder(); var packageDestination = Path.Combine(updateSandboxFolder, updatePackage.FileName); @@ -209,6 +218,19 @@ namespace NzbDrone.Core.Update return; } + + // Safety net, ConfigureUpdateMechanism should take care of invalid settings + if (_configFileProvider.UpdateMechanism == UpdateMechanism.BuiltIn && !_deploymentInfoProvider.BuiltInUpdaterAllowed) + { + _logger.ProgressDebug("Built-In updater disabled, please use {0} to install", _deploymentInfoProvider.PackageUpdateMechanism); + return; + } + else if (_configFileProvider.UpdateMechanism != UpdateMechanism.Script && !_deploymentInfoProvider.BuiltInUpdaterAllowed) + { + _logger.ProgressDebug("Update available, please use {0} to install", _deploymentInfoProvider.PackageUpdateMechanism); + return; + } + try { InstallUpdate(latestAvailable); diff --git a/src/NzbDrone.Core/Update/UpdateMechanism.cs b/src/NzbDrone.Core/Update/UpdateMechanism.cs index 8b647a1e7..f87dca79c 100644 --- a/src/NzbDrone.Core/Update/UpdateMechanism.cs +++ b/src/NzbDrone.Core/Update/UpdateMechanism.cs @@ -1,8 +1,11 @@ -namespace NzbDrone.Core.Update +namespace NzbDrone.Core.Update { public enum UpdateMechanism { BuiltIn = 0, - Script = 1 + Script = 1, + External = 10, + Apt = 11, + Docker = 12 } }