mirror of
https://github.com/Radarr/Radarr.git
synced 2024-09-11 03:52:33 +02:00
Fixed: Auto-Updater rollback logic tries to restore unchanged files.
This commit is contained in:
parent
09530b238f
commit
4bf3ef45b0
@ -24,6 +24,7 @@ private JSON()
|
||||
SerializeNullValues = false;
|
||||
UseOptimizedDatasetSchema = false;
|
||||
UsingGlobalTypes = false;
|
||||
UseUTCDateTime = true;
|
||||
}
|
||||
public bool UseOptimizedDatasetSchema = true;
|
||||
public bool UseFastGuid = true;
|
||||
@ -39,7 +40,7 @@ public string ToJSON(object obj)
|
||||
return ToJSON(obj, UseSerializerExtension, UseFastGuid, UseOptimizedDatasetSchema, SerializeNullValues);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public string ToJSON(object obj,
|
||||
bool enableSerializerExtensions,
|
||||
bool enableFastGuid,
|
||||
@ -49,13 +50,13 @@ public string ToJSON(object obj,
|
||||
return new JSONSerializer(enableOptimizedDatasetSchema, enableFastGuid, enableSerializerExtensions, serializeNullValues, IndentOutput).ConvertToJSON(obj);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public T ToObject<T>(string json)
|
||||
{
|
||||
return (T)ToObject(json, typeof(T));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public object ToObject(string json, Type type)
|
||||
{
|
||||
var ht = new JsonParser(json).Decode() as Dictionary<string, object>;
|
||||
@ -320,7 +321,7 @@ internal List<Getters> GetGetters(Type type)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
_getterscache.Add(type, getters);
|
||||
return getters;
|
||||
}
|
||||
@ -448,7 +449,7 @@ private object ParseDictionary(Dictionary<string, object> d, Dictionary<string,
|
||||
#if !SILVERLIGHT
|
||||
else if (pi.isDictionary || pi.isHashtable)
|
||||
oset = CreateDictionary((ArrayList)v, pi.pt, pi.GenericTypes, globaltypes);
|
||||
#else
|
||||
#else
|
||||
else if (pi.isDictionary)
|
||||
oset = CreateDictionary((List<object>)v, pi.pt, pi.GenericTypes, globaltypes);
|
||||
#endif
|
||||
@ -817,4 +818,4 @@ DataTable CreateDataTable(Dictionary<string, object> reader, Dictionary<string,
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,6 +170,8 @@ private void WriteDateTime(DateTime dateTime)
|
||||
_output.Append(dt.Minute.ToString("00", NumberFormatInfo.InvariantInfo));
|
||||
_output.Append(":");
|
||||
_output.Append(dt.Second.ToString("00", NumberFormatInfo.InvariantInfo));
|
||||
_output.Append(".");
|
||||
_output.Append(dt.Millisecond.ToString("000", NumberFormatInfo.InvariantInfo));
|
||||
|
||||
if (JSON.Instance.UseUTCDateTime)
|
||||
_output.Append("Z");
|
||||
|
@ -307,7 +307,7 @@ public void mode_verifyonly_should_verify_move()
|
||||
Subject.VerificationMode = DiskTransferVerificationMode.VerifyOnly;
|
||||
|
||||
Subject.TransferFile(_sourcePath, _targetPath, TransferMode.Move);
|
||||
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.GetFileSize(_sourcePath), Times.Once());
|
||||
|
||||
@ -688,6 +688,60 @@ public void should_throw_if_destination_is_readonly()
|
||||
Assert.Throws<IOException>(() => Subject.TransferFile(_sourcePath, _targetPath, TransferMode.Copy));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MirrorFolder_should_remove_additional_files()
|
||||
{
|
||||
WithRealDiskProvider();
|
||||
|
||||
var original = GetFilledTempFolder();
|
||||
var source = new DirectoryInfo(GetTempFilePath());
|
||||
var destination = new DirectoryInfo(GetTempFilePath());
|
||||
|
||||
source.Create();
|
||||
Subject.TransferFolder(original.FullName, destination.FullName, TransferMode.Copy);
|
||||
|
||||
var count = Subject.MirrorFolder(source.FullName, destination.FullName);
|
||||
|
||||
count.Should().Equals(0);
|
||||
destination.GetFileSystemInfos().Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MirrorFolder_should_add_new_files()
|
||||
{
|
||||
WithRealDiskProvider();
|
||||
|
||||
var original = GetFilledTempFolder();
|
||||
var source = new DirectoryInfo(GetTempFilePath());
|
||||
var destination = new DirectoryInfo(GetTempFilePath());
|
||||
|
||||
Subject.TransferFolder(original.FullName, source.FullName, TransferMode.Copy);
|
||||
|
||||
var count = Subject.MirrorFolder(source.FullName, destination.FullName);
|
||||
|
||||
count.Should().Equals(3);
|
||||
VerifyCopyFolder(original.FullName, destination.FullName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MirrorFolder_should_not_touch_equivalent_files()
|
||||
{
|
||||
WithRealDiskProvider();
|
||||
|
||||
var original = GetFilledTempFolder();
|
||||
var source = new DirectoryInfo(GetTempFilePath());
|
||||
var destination = new DirectoryInfo(GetTempFilePath());
|
||||
|
||||
Subject.TransferFolder(original.FullName, source.FullName, TransferMode.Copy);
|
||||
|
||||
Subject.TransferFolder(original.FullName, destination.FullName, TransferMode.Copy);
|
||||
|
||||
var count = Subject.MirrorFolder(source.FullName, destination.FullName);
|
||||
|
||||
count.Should().Equals(0);
|
||||
VerifyCopyFolder(original.FullName, destination.FullName);
|
||||
}
|
||||
|
||||
public DirectoryInfo GetFilledTempFolder()
|
||||
{
|
||||
var tempFolder = GetTempFilePath();
|
||||
@ -807,7 +861,10 @@ private void WithRealDiskProvider()
|
||||
if (File.Exists(d) && o) File.Delete(d);
|
||||
File.Move(s, d);
|
||||
});
|
||||
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.OpenReadStream(It.IsAny<string>()))
|
||||
.Returns<string>(s => new FileStream(s, FileMode.Open, FileAccess.Read));
|
||||
}
|
||||
|
||||
private void VerifyCopyFolder(string source, string destination)
|
||||
|
@ -15,6 +15,7 @@ public interface IDiskTransferService
|
||||
{
|
||||
TransferMode TransferFolder(string sourcePath, string targetPath, TransferMode mode, bool verified = true);
|
||||
TransferMode TransferFile(string sourcePath, string targetPath, TransferMode mode, bool overwrite = false, bool verified = true);
|
||||
int MirrorFolder(string sourcePath, string targetPath);
|
||||
}
|
||||
|
||||
public enum DiskTransferVerificationMode
|
||||
@ -83,6 +84,100 @@ public TransferMode TransferFolder(string sourcePath, string targetPath, Transfe
|
||||
return result;
|
||||
}
|
||||
|
||||
public int MirrorFolder(string sourcePath, string targetPath)
|
||||
{
|
||||
var filesCopied = 0;
|
||||
|
||||
Ensure.That(sourcePath, () => sourcePath).IsValidPath();
|
||||
Ensure.That(targetPath, () => targetPath).IsValidPath();
|
||||
|
||||
_logger.Debug("Mirror [{0}] > [{1}]", sourcePath, targetPath);
|
||||
|
||||
if (!_diskProvider.FolderExists(targetPath))
|
||||
{
|
||||
_diskProvider.CreateFolder(targetPath);
|
||||
}
|
||||
|
||||
var sourceFolders = _diskProvider.GetDirectoryInfos(sourcePath);
|
||||
var targetFolders = _diskProvider.GetDirectoryInfos(targetPath);
|
||||
|
||||
foreach (var subDir in targetFolders.Where(v => !sourceFolders.Any(d => d.Name == v.Name)))
|
||||
{
|
||||
_diskProvider.DeleteFolder(subDir.FullName, true);
|
||||
}
|
||||
|
||||
foreach (var subDir in sourceFolders)
|
||||
{
|
||||
filesCopied += MirrorFolder(subDir.FullName, Path.Combine(targetPath, subDir.Name));
|
||||
}
|
||||
|
||||
var sourceFiles = _diskProvider.GetFileInfos(sourcePath);
|
||||
var targetFiles = _diskProvider.GetFileInfos(targetPath);
|
||||
|
||||
foreach (var targetFile in targetFiles.Where(v => !sourceFiles.Any(d => d.Name == v.Name)))
|
||||
{
|
||||
_diskProvider.DeleteFile(targetFile.FullName);
|
||||
}
|
||||
|
||||
foreach (var sourceFile in sourceFiles)
|
||||
{
|
||||
var targetFile = Path.Combine(targetPath, sourceFile.Name);
|
||||
|
||||
if (CompareFiles(sourceFile.FullName, targetFile))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TransferFile(sourceFile.FullName, targetFile, TransferMode.Copy, true, true);
|
||||
filesCopied++;
|
||||
}
|
||||
|
||||
return filesCopied;
|
||||
}
|
||||
|
||||
private bool CompareFiles(string sourceFile, string targetFile)
|
||||
{
|
||||
if (!_diskProvider.FileExists(sourceFile) || !_diskProvider.FileExists(targetFile))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_diskProvider.GetFileSize(sourceFile) != _diskProvider.GetFileSize(targetFile))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var sourceBuffer = new byte[64 * 1024];
|
||||
var targetBuffer = new byte[64 * 1024];
|
||||
using (var sourceStream = _diskProvider.OpenReadStream(sourceFile))
|
||||
using (var targetStream = _diskProvider.OpenReadStream(targetFile))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var sourceLength = sourceStream.Read(sourceBuffer, 0, sourceBuffer.Length);
|
||||
var targetLength = targetStream.Read(targetBuffer, 0, targetBuffer.Length);
|
||||
|
||||
if (sourceLength != targetLength)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sourceLength == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (var i = 0; i < sourceLength; i++)
|
||||
{
|
||||
if (sourceBuffer[i] != targetBuffer[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TransferMode TransferFile(string sourcePath, string targetPath, TransferMode mode, bool overwrite = false, bool verified = true)
|
||||
{
|
||||
var verificationMode = verified ? VerificationMode : DiskTransferVerificationMode.None;
|
||||
@ -96,7 +191,7 @@ public TransferMode TransferFile(string sourcePath, string targetPath, TransferM
|
||||
Ensure.That(targetPath, () => targetPath).IsValidPath();
|
||||
|
||||
_logger.Debug("{0} [{1}] > [{2}]", mode, sourcePath, targetPath);
|
||||
|
||||
|
||||
var originalSize = _diskProvider.GetFileSize(sourcePath);
|
||||
|
||||
if (sourcePath == targetPath)
|
||||
|
@ -58,6 +58,7 @@
|
||||
<Compile Include="UpdateContainerBuilder.cs" />
|
||||
<Compile Include="UpdateEngine\BackupAndRestore.cs" />
|
||||
<Compile Include="UpdateEngine\BackupAppData.cs" />
|
||||
<Compile Include="UpdateEngine\DetectExistingVersion.cs" />
|
||||
<Compile Include="UpdateEngine\DetectApplicationType.cs" />
|
||||
<Compile Include="UpdateEngine\InstallUpdateService.cs" />
|
||||
<Compile Include="UpdateEngine\StartNzbDrone.cs" />
|
||||
|
@ -40,7 +40,6 @@ public static void Main(string[] args)
|
||||
|
||||
_container = UpdateContainerBuilder.Build(startupArgument);
|
||||
|
||||
Logger.Info("Updating Sonarr to version {0}", BuildInfo.Version);
|
||||
_container.Resolve<UpdateApp>().Start(args);
|
||||
|
||||
Logger.Info("Update completed successfully");
|
||||
@ -56,7 +55,6 @@ public void Start(string[] args)
|
||||
var startupContext = ParseArgs(args);
|
||||
var targetFolder = GetInstallationDirectory(startupContext);
|
||||
|
||||
Logger.Info("Starting update process. Target Path:{0}", targetFolder);
|
||||
_installUpdateService.Start(targetFolder, startupContext.ProcessId);
|
||||
}
|
||||
|
||||
|
@ -27,13 +27,14 @@ public BackupAndRestore(IDiskTransferService diskTransferService, IAppFolderInfo
|
||||
public void Backup(string source)
|
||||
{
|
||||
_logger.Info("Creating backup of existing installation");
|
||||
_diskTransferService.TransferFolder(source, _appFolderInfo.GetUpdateBackUpFolder(), TransferMode.Copy, false);
|
||||
_diskTransferService.MirrorFolder(source, _appFolderInfo.GetUpdateBackUpFolder());
|
||||
}
|
||||
|
||||
public void Restore(string target)
|
||||
{
|
||||
_logger.Info("Attempting to rollback upgrade");
|
||||
_diskTransferService.TransferFolder(_appFolderInfo.GetUpdateBackUpFolder(), target, TransferMode.Copy, false);
|
||||
var count = _diskTransferService.MirrorFolder(_appFolderInfo.GetUpdateBackUpFolder(), target);
|
||||
_logger.Info("Rolled back {0} files", count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,15 @@ public void Backup()
|
||||
_logger.Info("Backing up appdata (database/config)");
|
||||
var backupFolderAppData = _appFolderInfo.GetUpdateBackUpAppDataFolder();
|
||||
|
||||
_diskProvider.CreateFolder(backupFolderAppData);
|
||||
if (_diskProvider.FolderExists(backupFolderAppData))
|
||||
{
|
||||
_diskProvider.EmptyFolder(backupFolderAppData);
|
||||
}
|
||||
else
|
||||
{
|
||||
_diskProvider.CreateFolder(backupFolderAppData);
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
|
42
src/NzbDrone.Update/UpdateEngine/DetectExistingVersion.cs
Normal file
42
src/NzbDrone.Update/UpdateEngine/DetectExistingVersion.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using NLog;
|
||||
|
||||
namespace NzbDrone.Update.UpdateEngine
|
||||
{
|
||||
public interface IDetectExistingVersion
|
||||
{
|
||||
string GetExistingVersion(string targetFolder);
|
||||
}
|
||||
|
||||
public class DetectExistingVersion : IDetectExistingVersion
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
|
||||
public DetectExistingVersion(Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string GetExistingVersion(string targetFolder)
|
||||
{
|
||||
try
|
||||
{
|
||||
var targetExecutable = Path.Combine(targetFolder, "NzbDrone.exe");
|
||||
|
||||
if (File.Exists(targetExecutable))
|
||||
{
|
||||
var versionInfo = System.Diagnostics.FileVersionInfo.GetVersionInfo(targetExecutable);
|
||||
|
||||
return versionInfo.FileVersion;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, "Failed to get existing version from {0}", targetFolder);
|
||||
}
|
||||
|
||||
return "(unknown)";
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ public class InstallUpdateService : IInstallUpdateService
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IDiskTransferService _diskTransferService;
|
||||
private readonly IDetectApplicationType _detectApplicationType;
|
||||
private readonly IDetectExistingVersion _detectExistingVersion;
|
||||
private readonly ITerminateNzbDrone _terminateNzbDrone;
|
||||
private readonly IAppFolderInfo _appFolderInfo;
|
||||
private readonly IBackupAndRestore _backupAndRestore;
|
||||
@ -29,6 +30,7 @@ public class InstallUpdateService : IInstallUpdateService
|
||||
public InstallUpdateService(IDiskProvider diskProvider,
|
||||
IDiskTransferService diskTransferService,
|
||||
IDetectApplicationType detectApplicationType,
|
||||
IDetectExistingVersion detectExistingVersion,
|
||||
ITerminateNzbDrone terminateNzbDrone,
|
||||
IAppFolderInfo appFolderInfo,
|
||||
IBackupAndRestore backupAndRestore,
|
||||
@ -40,6 +42,7 @@ public InstallUpdateService(IDiskProvider diskProvider,
|
||||
_diskProvider = diskProvider;
|
||||
_diskTransferService = diskTransferService;
|
||||
_detectApplicationType = detectApplicationType;
|
||||
_detectExistingVersion = detectExistingVersion;
|
||||
_terminateNzbDrone = terminateNzbDrone;
|
||||
_appFolderInfo = appFolderInfo;
|
||||
_backupAndRestore = backupAndRestore;
|
||||
@ -76,30 +79,42 @@ private void Verify(string targetFolder, int processId)
|
||||
|
||||
public void Start(string installationFolder, int processId)
|
||||
{
|
||||
_logger.Info("Installation Folder: {0}", installationFolder);
|
||||
_logger.Info("Updating Sonarr from version {0} to version {1}", _detectExistingVersion.GetExistingVersion(installationFolder), BuildInfo.Version);
|
||||
|
||||
Verify(installationFolder, processId);
|
||||
|
||||
var appType = _detectApplicationType.GetAppType();
|
||||
|
||||
_processProvider.FindProcessByName(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME);
|
||||
_processProvider.FindProcessByName(ProcessProvider.NZB_DRONE_PROCESS_NAME);
|
||||
|
||||
if (OsInfo.IsWindows)
|
||||
{
|
||||
_terminateNzbDrone.Terminate(processId);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_processProvider.FindProcessByName(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME);
|
||||
_processProvider.FindProcessByName(ProcessProvider.NZB_DRONE_PROCESS_NAME);
|
||||
_backupAndRestore.Backup(installationFolder);
|
||||
_backupAppData.Backup();
|
||||
|
||||
if (OsInfo.IsWindows)
|
||||
{
|
||||
_terminateNzbDrone.Terminate(processId);
|
||||
if (_processProvider.Exists(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME) || _processProvider.Exists(ProcessProvider.NZB_DRONE_PROCESS_NAME))
|
||||
{
|
||||
_logger.Error("Sonarr was restarted prematurely by external process.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_backupAndRestore.Backup(installationFolder);
|
||||
_backupAppData.Backup();
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Info("Emptying installation folder");
|
||||
_diskProvider.EmptyFolder(installationFolder);
|
||||
|
||||
_logger.Info("Copying new files to target folder");
|
||||
_diskTransferService.TransferFolder(_appFolderInfo.GetUpdatePackageFolder(), installationFolder, TransferMode.Copy, false);
|
||||
_diskTransferService.MirrorFolder(_appFolderInfo.GetUpdatePackageFolder(), installationFolder);
|
||||
|
||||
// Set executable flag on Sonarr app
|
||||
if (OsInfo.IsOsx)
|
||||
@ -109,8 +124,9 @@ public void Start(string installationFolder, int processId)
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Fatal(e, "Failed to copy upgrade package to target folder.");
|
||||
_logger.Error(e, "Failed to copy upgrade package to target folder.");
|
||||
_backupAndRestore.Restore(installationFolder);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
Loading…
Reference in New Issue
Block a user