Fixed: Auto-Updater rollback logic tries to restore unchanged files.

This commit is contained in:
Taloth Saldono 2016-09-09 01:05:27 +02:00
parent 09530b238f
commit 4bf3ef45b0
10 changed files with 244 additions and 23 deletions

View File

@ -24,6 +24,7 @@ namespace LogentriesNLog.fastJSON
SerializeNullValues = false;
UseOptimizedDatasetSchema = false;
UsingGlobalTypes = false;
UseUTCDateTime = true;
}
public bool UseOptimizedDatasetSchema = true;
public bool UseFastGuid = true;
@ -39,7 +40,7 @@ namespace LogentriesNLog.fastJSON
return ToJSON(obj, UseSerializerExtension, UseFastGuid, UseOptimizedDatasetSchema, SerializeNullValues);
}
public string ToJSON(object obj,
bool enableSerializerExtensions,
bool enableFastGuid,
@ -49,13 +50,13 @@ namespace LogentriesNLog.fastJSON
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 @@ namespace LogentriesNLog.fastJSON
}
}
_getterscache.Add(type, getters);
return getters;
}
@ -448,7 +449,7 @@ namespace LogentriesNLog.fastJSON
#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 @@ namespace LogentriesNLog.fastJSON
}
#endif
}
}
}

View File

@ -170,6 +170,8 @@ namespace LogentriesNLog.fastJSON
_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");

View File

@ -307,7 +307,7 @@ namespace NzbDrone.Common.Test.DiskTests
Subject.VerificationMode = DiskTransferVerificationMode.VerifyOnly;
Subject.TransferFile(_sourcePath, _targetPath, TransferMode.Move);
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.GetFileSize(_sourcePath), Times.Once());
@ -688,6 +688,60 @@ namespace NzbDrone.Common.Test.DiskTests
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 @@ namespace NzbDrone.Common.Test.DiskTests
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)

View File

@ -15,6 +15,7 @@ namespace NzbDrone.Common.Disk
{
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 @@ namespace NzbDrone.Common.Disk
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 @@ namespace NzbDrone.Common.Disk
Ensure.That(targetPath, () => targetPath).IsValidPath();
_logger.Debug("{0} [{1}] > [{2}]", mode, sourcePath, targetPath);
var originalSize = _diskProvider.GetFileSize(sourcePath);
if (sourcePath == targetPath)

View File

@ -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" />

View File

@ -40,7 +40,6 @@ namespace NzbDrone.Update
_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 @@ namespace NzbDrone.Update
var startupContext = ParseArgs(args);
var targetFolder = GetInstallationDirectory(startupContext);
Logger.Info("Starting update process. Target Path:{0}", targetFolder);
_installUpdateService.Start(targetFolder, startupContext.ProcessId);
}

View File

@ -27,13 +27,14 @@ namespace NzbDrone.Update.UpdateEngine
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);
}
}
}
}

View File

@ -34,7 +34,15 @@ namespace NzbDrone.Update.UpdateEngine
_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
{

View 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)";
}
}
}

View File

@ -18,6 +18,7 @@ namespace NzbDrone.Update.UpdateEngine
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 @@ namespace NzbDrone.Update.UpdateEngine
public InstallUpdateService(IDiskProvider diskProvider,
IDiskTransferService diskTransferService,
IDetectApplicationType detectApplicationType,
IDetectExistingVersion detectExistingVersion,
ITerminateNzbDrone terminateNzbDrone,
IAppFolderInfo appFolderInfo,
IBackupAndRestore backupAndRestore,
@ -40,6 +42,7 @@ namespace NzbDrone.Update.UpdateEngine
_diskProvider = diskProvider;
_diskTransferService = diskTransferService;
_detectApplicationType = detectApplicationType;
_detectExistingVersion = detectExistingVersion;
_terminateNzbDrone = terminateNzbDrone;
_appFolderInfo = appFolderInfo;
_backupAndRestore = backupAndRestore;
@ -76,30 +79,42 @@ namespace NzbDrone.Update.UpdateEngine
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 @@ namespace NzbDrone.Update.UpdateEngine
}
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