From ebd13bdda8791e71cfd92cd85bb1983ee8cd9425 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 7 Jun 2014 12:19:37 -0700 Subject: [PATCH] New: Health check for AppData and Startup folder conflict --- .../Checks/AppDataLocationFixture.cs | 54 +++++++++++++++++++ .../HealthCheck/Checks/UpdateCheckFixture.cs | 23 +++++++- .../NzbDrone.Core.Test.csproj | 1 + .../Checks/AppDataLocationCheck.cs | 34 ++++++++++++ .../HealthCheck/Checks/UpdateCheck.cs | 14 +++-- src/NzbDrone.Core/NzbDrone.Core.csproj | 1 + .../Update/InstallUpdateService.cs | 11 ++++ 7 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 src/NzbDrone.Core.Test/HealthCheck/Checks/AppDataLocationFixture.cs create mode 100644 src/NzbDrone.Core/HealthCheck/Checks/AppDataLocationCheck.cs diff --git a/src/NzbDrone.Core.Test/HealthCheck/Checks/AppDataLocationFixture.cs b/src/NzbDrone.Core.Test/HealthCheck/Checks/AppDataLocationFixture.cs new file mode 100644 index 000000000..ade6278e3 --- /dev/null +++ b/src/NzbDrone.Core.Test/HealthCheck/Checks/AppDataLocationFixture.cs @@ -0,0 +1,54 @@ +using NUnit.Framework; +using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Core.HealthCheck.Checks; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.HealthCheck.Checks +{ + [TestFixture] + public class AppDataLocationFixture : CoreTest + { + [Test] + public void should_return_warning_when_app_data_is_child_of_startup_folder() + { + Mocker.GetMock() + .Setup(s => s.StartUpFolder) + .Returns(@"C:\NzbDrone".AsOsAgnostic()); + + Mocker.GetMock() + .Setup(s => s.AppDataFolder) + .Returns(@"C:\NzbDrone\AppData".AsOsAgnostic()); + + Subject.Check().ShouldBeWarning(); + } + + [Test] + public void should_return_warning_when_app_data_is_same_as_startup_folder() + { + Mocker.GetMock() + .Setup(s => s.StartUpFolder) + .Returns(@"C:\NzbDrone".AsOsAgnostic()); + + Mocker.GetMock() + .Setup(s => s.AppDataFolder) + .Returns(@"C:\NzbDrone".AsOsAgnostic()); + + Subject.Check().ShouldBeWarning(); + } + + [Test] + public void should_return_ok_when_no_conflict() + { + Mocker.GetMock() + .Setup(s => s.StartUpFolder) + .Returns(@"C:\NzbDrone".AsOsAgnostic()); + + Mocker.GetMock() + .Setup(s => s.AppDataFolder) + .Returns(@"C:\ProgramData\NzbDrone".AsOsAgnostic()); + + Subject.Check().ShouldBeOk(); + } + } +} diff --git a/src/NzbDrone.Core.Test/HealthCheck/Checks/UpdateCheckFixture.cs b/src/NzbDrone.Core.Test/HealthCheck/Checks/UpdateCheckFixture.cs index 7e6fd213c..68123676a 100644 --- a/src/NzbDrone.Core.Test/HealthCheck/Checks/UpdateCheckFixture.cs +++ b/src/NzbDrone.Core.Test/HealthCheck/Checks/UpdateCheckFixture.cs @@ -1,10 +1,9 @@ using System; -using FluentAssertions; using Moq; using NUnit.Framework; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; -using NzbDrone.Core.HealthCheck; +using NzbDrone.Core.Configuration; using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.Test.Framework; @@ -28,5 +27,25 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks Subject.Check().ShouldBeError(); } + + [Test] + public void should_return_error_when_app_folder_is_write_protected_and_update_automatically_is_enabled() + { + MonoOnly(); + + Mocker.GetMock() + .Setup(s => s.UpdateAutomatically) + .Returns(true); + + Mocker.GetMock() + .Setup(s => s.StartUpFolder) + .Returns(@"/opt/nzbdrone"); + + Mocker.GetMock() + .Setup(s => s.WriteAllText(It.IsAny(), It.IsAny())) + .Throws(); + + Subject.Check().ShouldBeError(); + } } } diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 862f9cd46..818726309 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -126,6 +126,7 @@ + diff --git a/src/NzbDrone.Core/HealthCheck/Checks/AppDataLocationCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/AppDataLocationCheck.cs new file mode 100644 index 000000000..7567ef095 --- /dev/null +++ b/src/NzbDrone.Core/HealthCheck/Checks/AppDataLocationCheck.cs @@ -0,0 +1,34 @@ +using NzbDrone.Common; +using NzbDrone.Common.EnvironmentInfo; + +namespace NzbDrone.Core.HealthCheck.Checks +{ + public class AppDataLocationCheck : HealthCheckBase + { + private readonly IAppFolderInfo _appFolderInfo; + + public AppDataLocationCheck(IAppFolderInfo appFolderInfo) + { + _appFolderInfo = appFolderInfo; + } + + public override HealthCheck Check() + { + if (_appFolderInfo.StartUpFolder.IsParentPath(_appFolderInfo.AppDataFolder) || + _appFolderInfo.StartUpFolder.PathEquals(_appFolderInfo.AppDataFolder)) + { + return new HealthCheck(GetType(), HealthCheckResult.Warning, "Updating will not be possible to prevent deleting AppData on Update"); + } + + return new HealthCheck(GetType()); + } + + public override bool CheckOnConfigChange + { + get + { + return false; + } + } + } +} diff --git a/src/NzbDrone.Core/HealthCheck/Checks/UpdateCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/UpdateCheck.cs index 350595138..704eb2799 100644 --- a/src/NzbDrone.Core/HealthCheck/Checks/UpdateCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/Checks/UpdateCheck.cs @@ -2,6 +2,7 @@ using System.IO; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Core.Configuration; using NzbDrone.Core.Update; namespace NzbDrone.Core.HealthCheck.Checks @@ -11,19 +12,22 @@ namespace NzbDrone.Core.HealthCheck.Checks private readonly IDiskProvider _diskProvider; private readonly IAppFolderInfo _appFolderInfo; private readonly ICheckUpdateService _checkUpdateService; + private readonly IConfigFileProvider _configFileProvider; - public UpdateCheck(IDiskProvider diskProvider, IAppFolderInfo appFolderInfo, ICheckUpdateService checkUpdateService) + public UpdateCheck(IDiskProvider diskProvider, + IAppFolderInfo appFolderInfo, + ICheckUpdateService checkUpdateService, + IConfigFileProvider configFileProvider) { _diskProvider = diskProvider; _appFolderInfo = appFolderInfo; _checkUpdateService = checkUpdateService; + _configFileProvider = configFileProvider; } - - + public override HealthCheck Check() { - //TODO: Check on mono as well - if (OsInfo.IsWindows) + if (OsInfo.IsWindows || (OsInfo.IsMono && _configFileProvider.UpdateAutomatically)) { try { diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 76f49c4cd..7dcfdebe4 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -285,6 +285,7 @@ + diff --git a/src/NzbDrone.Core/Update/InstallUpdateService.cs b/src/NzbDrone.Core/Update/InstallUpdateService.cs index 17d32991b..0e89f3116 100644 --- a/src/NzbDrone.Core/Update/InstallUpdateService.cs +++ b/src/NzbDrone.Core/Update/InstallUpdateService.cs @@ -60,6 +60,8 @@ namespace NzbDrone.Core.Update { try { + EnsureAppDataSafety(); + var updateSandboxFolder = _appFolderInfo.GetUpdateSandboxFolder(); var packageDestination = Path.Combine(updateSandboxFolder, updatePackage.FileName); @@ -139,6 +141,15 @@ namespace NzbDrone.Core.Update return String.Join(" ", processId, updateSandboxFolder.TrimEnd(Path.DirectorySeparatorChar).WrapInQuotes(), executingApplication.WrapInQuotes()); } + private void EnsureAppDataSafety() + { + if (_appFolderInfo.StartUpFolder.IsParentPath(_appFolderInfo.AppDataFolder) || + _appFolderInfo.StartUpFolder.PathEquals(_appFolderInfo.AppDataFolder)) + { + throw new NotSupportedException("Update will cause AppData to be deleted, correct you configuration before proceeding"); + } + } + public void Execute(ApplicationUpdateCommand message) { _logger.ProgressDebug("Checking for updates");