You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Lidarr/src/NzbDrone.Core/Update/InstallUpdateService.cs

278 lines
12 KiB

using System;
using System.Collections.Generic;
using System.IO;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Common.Processes;
using NzbDrone.Core.Backup;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Update.Commands;
namespace NzbDrone.Core.Update
{
public class InstallUpdateService : IExecute<ApplicationUpdateCommand>
{
private readonly ICheckUpdateService _checkUpdateService;
private readonly Logger _logger;
private readonly IAppFolderInfo _appFolderInfo;
private readonly IDiskProvider _diskProvider;
private readonly IDiskTransferService _diskTransferService;
private readonly IHttpClient _httpClient;
private readonly IArchiveService _archiveService;
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;
private readonly IOsInfo _osInfo;
public InstallUpdateService(ICheckUpdateService checkUpdateService,
IAppFolderInfo appFolderInfo,
IDiskProvider diskProvider,
IDiskTransferService diskTransferService,
IHttpClient httpClient,
IArchiveService archiveService,
IProcessProvider processProvider,
IVerifyUpdates updateVerifier,
IStartupContext startupContext,
IDeploymentInfoProvider deploymentInfoProvider,
IConfigFileProvider configFileProvider,
IRuntimeInfo runtimeInfo,
IBackupService backupService,
IOsInfo osInfo,
Logger logger)
{
if (configFileProvider == null)
{
throw new ArgumentNullException(nameof(configFileProvider));
}
_checkUpdateService = checkUpdateService;
_appFolderInfo = appFolderInfo;
_diskProvider = diskProvider;
_diskTransferService = diskTransferService;
_httpClient = httpClient;
_archiveService = archiveService;
_processProvider = processProvider;
_updateVerifier = updateVerifier;
_startupContext = startupContext;
_deploymentInfoProvider = deploymentInfoProvider;
_configFileProvider = configFileProvider;
_runtimeInfo = runtimeInfo;
_backupService = backupService;
_osInfo = osInfo;
_logger = logger;
}
private void InstallUpdate(UpdatePackage updatePackage)
{
EnsureAppDataSafety();
if (OsInfo.IsWindows || _configFileProvider.UpdateMechanism != UpdateMechanism.Script)
{
var startupFolder = _appFolderInfo.StartUpFolder;
var uiFolder = Path.Combine(startupFolder, "UI");
if (!_diskProvider.FolderWritable(startupFolder))
{
throw new UpdateFolderNotWritableException("Cannot install update because startup folder '{0}' is not writable by the user '{1}'.", startupFolder, Environment.UserName);
}
if (!_diskProvider.FolderWritable(uiFolder))
{
throw new UpdateFolderNotWritableException("Cannot install update because UI folder '{0}' is not writable by the user '{1}'.", uiFolder, Environment.UserName);
}
}
var updateSandboxFolder = _appFolderInfo.GetUpdateSandboxFolder();
var packageDestination = Path.Combine(updateSandboxFolder, updatePackage.FileName);
if (_diskProvider.FolderExists(updateSandboxFolder))
{
_logger.Info("Deleting old update files");
_diskProvider.DeleteFolder(updateSandboxFolder, true);
}
_logger.ProgressInfo("Downloading update {0}", updatePackage.Version);
_logger.Debug("Downloading update package from [{0}] to [{1}]", updatePackage.Url, packageDestination);
_httpClient.DownloadFile(updatePackage.Url, packageDestination);
_logger.ProgressInfo("Verifying update package");
if (!_updateVerifier.Verify(updatePackage, packageDestination))
{
_logger.Error("Update package is invalid");
throw new UpdateVerificationFailedException("Update file '{0}' is invalid", packageDestination);
}
_logger.Info("Update package verified successfully");
_logger.ProgressInfo("Extracting Update package");
_archiveService.Extract(packageDestination, updateSandboxFolder);
_logger.Info("Update package extracted successfully");
EnsureValidBranch(updatePackage);
_backupService.Backup(BackupType.Update);
if (OsInfo.IsNotWindows && _configFileProvider.UpdateMechanism == UpdateMechanism.Script)
{
InstallUpdateWithScript(updateSandboxFolder);
return;
}
_logger.Info("Preparing client");
_diskTransferService.TransferFolder(_appFolderInfo.GetUpdateClientFolder(), updateSandboxFolder, TransferMode.Move, false);
// Set executable flag on update app
<<<<<<< HEAD
if (OsInfo.IsOsx)
{
_diskProvider.SetPermissions(_appFolderInfo.GetUpdateClientExePath(updatePackage.Version), "0755", null, null);
}
_logger.Info("Starting update client {0}", _appFolderInfo.GetUpdateClientExePath(updatePackage.Version));
_logger.ProgressInfo("Lidarr will restart shortly.");
_processProvider.Start(_appFolderInfo.GetUpdateClientExePath(updatePackage.Version), GetUpdaterArgs(updateSandboxFolder));
=======
if (OsInfo.IsOsx || (OsInfo.IsLinux && PlatformInfo.IsNetCore))
{
_diskProvider.SetPermissions(_appFolderInfo.GetUpdateClientExePath(updatePackage.Runtime), "0755", null, null);
}
_logger.Info("Starting update client {0}", _appFolderInfo.GetUpdateClientExePath(updatePackage.Runtime));
_logger.ProgressInfo("Lidarr will restart shortly.");
_processProvider.Start(_appFolderInfo.GetUpdateClientExePath(updatePackage.Runtime), GetUpdaterArgs(updateSandboxFolder));
>>>>>>> 4346c76d4... New: Multi target net framework 4.6.2 and net core 3.0
}
private void EnsureValidBranch(UpdatePackage package)
{
var currentBranch = _configFileProvider.Branch;
if (package.Branch != currentBranch)
{
try
{
_logger.Info("Branch [{0}] is being redirected to [{1}]]", currentBranch, package.Branch);
var config = new Dictionary<string, object>();
config["Branch"] = package.Branch;
_configFileProvider.SaveConfigDictionary(config);
}
catch (Exception e)
{
_logger.Error(e, "Couldn't change the branch from [{0}] to [{1}].", currentBranch, package.Branch);
}
}
}
private void InstallUpdateWithScript(string updateSandboxFolder)
{
var scriptPath = _configFileProvider.UpdateScriptPath;
if (scriptPath.IsNullOrWhiteSpace())
{
throw new UpdateFailedException("Update Script has not been defined");
}
if (!_diskProvider.FileExists(scriptPath, StringComparison.Ordinal))
{
throw new UpdateFailedException("Update Script: '{0}' does not exist", scriptPath);
}
_logger.Info("Removing Lidarr.Update");
_diskProvider.DeleteFolder(_appFolderInfo.GetUpdateClientFolder(), true);
_logger.ProgressInfo("Starting update script: {0}", _configFileProvider.UpdateScriptPath);
_processProvider.Start(scriptPath, GetUpdaterArgs(updateSandboxFolder));
}
private string GetUpdaterArgs(string updateSandboxFolder)
{
var processId = _processProvider.GetCurrentProcess().Id.ToString();
var executingApplication = _runtimeInfo.ExecutingApplication;
var args = string.Join(" ", processId, updateSandboxFolder.TrimEnd(Path.DirectorySeparatorChar).WrapInQuotes(), executingApplication.WrapInQuotes(), _startupContext.PreservedArguments);
_logger.Info("Updater Arguments: " + args);
return args;
}
private void EnsureAppDataSafety()
{
if (_appFolderInfo.StartUpFolder.IsParentPath(_appFolderInfo.AppDataFolder) ||
_appFolderInfo.StartUpFolder.PathEquals(_appFolderInfo.AppDataFolder))
{
throw new UpdateFailedException("Your Lidarr configuration '{0}' is being stored in application folder '{1}' which will cause data lost during the upgrade. Please remove any symlinks or redirects before trying again.", _appFolderInfo.AppDataFolder, _appFolderInfo.StartUpFolder);
}
}
public void Execute(ApplicationUpdateCommand message)
{
_logger.ProgressDebug("Checking for updates");
var latestAvailable = _checkUpdateService.AvailableUpdate();
if (latestAvailable == null)
{
_logger.ProgressDebug("No update available");
return;
}
if (_osInfo.IsDocker)
{
_logger.ProgressDebug("Updating is disabled inside a docker container. Please update the container image.");
return;
}
if (OsInfo.IsNotWindows && !_configFileProvider.UpdateAutomatically && message.Trigger != CommandTrigger.Manual)
{
_logger.ProgressDebug("Auto-update not enabled, not installing available update");
return;
}
// Safety net, ConfigureUpdateMechanism should take care of invalid settings
if (_configFileProvider.UpdateMechanism == UpdateMechanism.BuiltIn && _deploymentInfoProvider.IsExternalUpdateMechanism)
{
_logger.ProgressDebug("Built-In updater disabled, please use {0} to install", _deploymentInfoProvider.PackageUpdateMechanism);
return;
}
else if (_configFileProvider.UpdateMechanism != UpdateMechanism.Script && _deploymentInfoProvider.IsExternalUpdateMechanism)
{
_logger.ProgressDebug("Update available, please use {0} to install", _deploymentInfoProvider.PackageUpdateMechanism);
return;
}
try
{
InstallUpdate(latestAvailable);
_logger.ProgressDebug("Restarting Lidarr to apply updates");
}
catch (UpdateFolderNotWritableException ex)
{
_logger.Error(ex, "Update process failed");
throw new CommandFailedException("Startup folder not writable by user '{0}'", ex, Environment.UserName);
}
catch (UpdateVerificationFailedException ex)
{
_logger.Error(ex, "Update process failed");
throw new CommandFailedException("Downloaded update package is corrupt", ex);
}
catch (UpdateFailedException ex)
{
_logger.Error(ex, "Update process failed");
throw new CommandFailedException(ex);
}
}
}
}