fixed apptype detection during update

This commit is contained in:
kay.one 2013-05-20 21:03:05 -07:00
parent 2573558321
commit 99f269cd95
11 changed files with 300 additions and 187 deletions

View File

@ -5,38 +5,21 @@ using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Model;
using NzbDrone.Test.Common;
using NzbDrone.Update.Providers;
using NzbDrone.Update.UpdateEngine;
namespace NzbDrone.Update.Test
{
[TestFixture]
public class ProgramFixture : TestBase
public class ProgramFixture : TestBase<Program>
{
private Program _program;
[SetUp]
public void Setup()
{
_program = Mocker.Resolve<Program>();
}
[Test]
public void should_throw_if_null_passed_in()
{
Assert.Throws<ArgumentException>(() => _program.Start(null));
Assert.Throws<ArgumentOutOfRangeException>(() => Subject.Start(null));
}
[Test]
public void should_throw_if_less_than_two_arguments_arent_passed_in()
{
Assert.Throws<ArgumentException>(() => _program.Start(new[] { "" }));
}
[Test]
public void should_throw_if_more_than_two_arguments_arent_passed_in()
{
Assert.Throws<ArgumentException>(() => _program.Start(new[] { "", "", "" }));
}
[TestCase("d", "")]
[TestCase("", "")]
@ -46,7 +29,7 @@ namespace NzbDrone.Update.Test
[TestCase(".", "")]
public void should_throw_if_first_arg_isnt_an_int(string arg1, string arg2)
{
Assert.Throws<ArgumentOutOfRangeException>(() => _program.Start(new[] { arg1, arg2 }));
Assert.Throws<ArgumentOutOfRangeException>(() => Subject.Start(new[] { arg1, arg2 }));
}
[Test]
@ -57,11 +40,11 @@ namespace NzbDrone.Update.Test
Mocker.GetMock<IProcessProvider>().Setup(c => c.GetProcessById(12))
.Returns(new ProcessInfo() { StartPath = ProcessPath });
_program.Start(new[] { "12", "" });
Mocker.GetMock<UpdateProvider>().Verify(c => c.Start(@"C:\NzbDrone"), Times.Once());
Subject.Start(new[] { "12", "" });
Mocker.GetMock<IInstallUpdateService>().Verify(c => c.Start(@"C:\NzbDrone"), Times.Once());
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
/*
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
@ -7,7 +8,7 @@ using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Model;
using NzbDrone.Test.Common;
using NzbDrone.Update.Providers;
using NzbDrone.Update.UpdateEngine;
namespace NzbDrone.Update.Test
{
@ -58,7 +59,7 @@ namespace NzbDrone.Update.Test
WithServiceRunning(true);
Mocker.Resolve<UpdateProvider>().Start(TARGET_FOLDER);
Mocker.Resolve<InstallUpdateService>().Start(TARGET_FOLDER);
Mocker.GetMock<IServiceProvider>().Verify(c => c.Stop(ServiceProvider.NZBDRONE_SERVICE_NAME), Times.Once());
@ -71,7 +72,7 @@ namespace NzbDrone.Update.Test
WithServiceRunning(false);
Mocker.Resolve<UpdateProvider>().Start(TARGET_FOLDER);
Mocker.Resolve<InstallUpdateService>().Start(TARGET_FOLDER);
Mocker.GetMock<IServiceProvider>().Verify(c => c.Stop(ServiceProvider.NZBDRONE_SERVICE_NAME), Times.Never());
@ -81,7 +82,7 @@ namespace NzbDrone.Update.Test
public void should_not_stop_nzbdrone_service_if_service_isnt_installed()
{
Mocker.Resolve<UpdateProvider>().Start(TARGET_FOLDER);
Mocker.Resolve<InstallUpdateService>().Start(TARGET_FOLDER);
Mocker.GetMock<IServiceProvider>().Verify(c => c.Stop(It.IsAny<string>()), Times.Never());
@ -97,7 +98,7 @@ namespace NzbDrone.Update.Test
.Returns(proccesses);
Mocker.Resolve<UpdateProvider>().Start(TARGET_FOLDER);
Mocker.Resolve<InstallUpdateService>().Start(TARGET_FOLDER);
Mocker.GetMock<IProcessProvider>().Verify(c => c.KillAll(ProcessProvider.NzbDroneProcessName), Times.Once());
@ -111,7 +112,7 @@ namespace NzbDrone.Update.Test
.Returns(new List<ProcessInfo>());
Mocker.Resolve<UpdateProvider>().Start(TARGET_FOLDER);
Mocker.Resolve<InstallUpdateService>().Start(TARGET_FOLDER);
Mocker.GetMock<IProcessProvider>().Verify(c => c.Kill(It.IsAny<int>()), Times.Never());
@ -123,7 +124,7 @@ namespace NzbDrone.Update.Test
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.CopyDirectory(TARGET_FOLDER, BACKUP_FOLDER));
Mocker.Resolve<UpdateProvider>().Start(TARGET_FOLDER);
Mocker.Resolve<InstallUpdateService>().Start(TARGET_FOLDER);
}
[Test]
@ -135,7 +136,7 @@ namespace NzbDrone.Update.Test
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.DeleteFolder(UPDATE_FOLDER, true));
Mocker.Resolve<UpdateProvider>().Start(TARGET_FOLDER);
Mocker.Resolve<InstallUpdateService>().Start(TARGET_FOLDER);
}
[Test]
@ -146,7 +147,7 @@ namespace NzbDrone.Update.Test
.Throws(new IOException());
Mocker.Resolve<UpdateProvider>().Start(TARGET_FOLDER);
Mocker.Resolve<InstallUpdateService>().Start(TARGET_FOLDER);
Mocker.GetMock<IDiskProvider>()
@ -161,7 +162,7 @@ namespace NzbDrone.Update.Test
WithServiceRunning(true);
Mocker.Resolve<UpdateProvider>().Start(TARGET_FOLDER);
Mocker.Resolve<InstallUpdateService>().Start(TARGET_FOLDER);
VerifyServiceRestart();
@ -174,7 +175,7 @@ namespace NzbDrone.Update.Test
WithServiceRunning(false);
Mocker.Resolve<UpdateProvider>().Start(TARGET_FOLDER);
Mocker.Resolve<InstallUpdateService>().Start(TARGET_FOLDER);
VerifyProcessRestart();
@ -191,7 +192,7 @@ namespace NzbDrone.Update.Test
.Throws(new IOException());
Mocker.Resolve<UpdateProvider>().Start(TARGET_FOLDER);
Mocker.Resolve<InstallUpdateService>().Start(TARGET_FOLDER);
VerifyServiceRestart();
@ -209,7 +210,7 @@ namespace NzbDrone.Update.Test
.Throws(new IOException());
Mocker.Resolve<UpdateProvider>().Start(TARGET_FOLDER);
Mocker.Resolve<InstallUpdateService>().Start(TARGET_FOLDER);
VerifyProcessRestart();
@ -237,3 +238,4 @@ namespace NzbDrone.Update.Test
}
}
*/

View File

@ -4,7 +4,7 @@ using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Test.Common;
using NzbDrone.Update.Providers;
using NzbDrone.Update.UpdateEngine;
namespace NzbDrone.Update.Test
{
@ -25,7 +25,7 @@ namespace NzbDrone.Update.Test
[TestCase(" ")]
public void update_should_throw_target_folder_is_blank(string target)
{
Assert.Throws<ArgumentException>(() => Mocker.Resolve<UpdateProvider>().Start(target))
Assert.Throws<ArgumentException>(() => Mocker.Resolve<InstallUpdateService>().Start(target))
.Message.Should().StartWith("Target folder can not be null or empty");
}
@ -34,7 +34,7 @@ namespace NzbDrone.Update.Test
{
string targetFolder = "c:\\NzbDrone\\";
Assert.Throws<DirectoryNotFoundException>(() => Mocker.Resolve<UpdateProvider>().Start(targetFolder))
Assert.Throws<DirectoryNotFoundException>(() => Mocker.Resolve<InstallUpdateService>().Start(targetFolder))
.Message.Should().StartWith("Target folder doesn't exist");
}
@ -52,7 +52,7 @@ namespace NzbDrone.Update.Test
.Setup(c => c.FolderExists(sandboxFolder))
.Returns(false);
Assert.Throws<DirectoryNotFoundException>(() => Mocker.Resolve<UpdateProvider>().Start(targetFolder))
Assert.Throws<DirectoryNotFoundException>(() => Mocker.Resolve<InstallUpdateService>().Start(targetFolder))
.Message.Should().StartWith("Update folder doesn't exist");
}
}

View File

@ -69,8 +69,12 @@
<Compile Include="AppType.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Providers\UpdateProvider.cs" />
<Compile Include="UpdateContainerBuilder.cs" />
<Compile Include="UpdateEngine\BackupAndRestore.cs" />
<Compile Include="UpdateEngine\DetectApplicationType.cs" />
<Compile Include="UpdateEngine\InstallUpdateService.cs" />
<Compile Include="UpdateEngine\StartNzbDrone.cs" />
<Compile Include="UpdateEngine\TerminateNzbDrone.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
@ -89,6 +93,7 @@
<Name>NzbDrone.Common</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@ -3,21 +3,21 @@ using System.IO;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Composition;
using NzbDrone.Update.Providers;
using NzbDrone.Update.UpdateEngine;
namespace NzbDrone.Update
{
public class Program
{
private readonly UpdateProvider _updateProvider;
private readonly IInstallUpdateService _installUpdateService;
private readonly IProcessProvider _processProvider;
private static IContainer _container;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public Program(UpdateProvider updateProvider, IProcessProvider processProvider)
public Program(IInstallUpdateService installUpdateService, IProcessProvider processProvider)
{
_updateProvider = updateProvider;
_installUpdateService = installUpdateService;
_processProvider = processProvider;
}
@ -47,13 +47,13 @@ namespace NzbDrone.Update
string targetFolder = exeFileInfo.Directory.FullName;
logger.Info("Starting update process. Target Path:{0}", targetFolder);
_updateProvider.Start(targetFolder);
_installUpdateService.Start(targetFolder);
}
private int ParseProcessId(string[] args)
{
int id;
if (!Int32.TryParse(args[0], out id) || id <= 0)
if (args ==null || !Int32.TryParse(args[0], out id) || id <= 0)
{
throw new ArgumentOutOfRangeException("Invalid process id: " + args[0]);
}

View File

@ -1,137 +0,0 @@
using System;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common;
using IServiceProvider = NzbDrone.Common.IServiceProvider;
namespace NzbDrone.Update.Providers
{
public class UpdateProvider
{
private readonly IDiskProvider _diskProvider;
private readonly IServiceProvider _serviceProvider;
private readonly IProcessProvider _processProvider;
private readonly IEnvironmentProvider _environmentProvider;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public UpdateProvider(IDiskProvider diskProvider, IServiceProvider serviceProvider,
IProcessProvider processProvider, IEnvironmentProvider environmentProvider)
{
_diskProvider = diskProvider;
_serviceProvider = serviceProvider;
_processProvider = processProvider;
_environmentProvider = environmentProvider;
}
public UpdateProvider()
{
}
private void Verify(string targetFolder)
{
logger.Info("Verifying requirements before update...");
if (String.IsNullOrWhiteSpace(targetFolder))
throw new ArgumentException("Target folder can not be null or empty");
if (!_diskProvider.FolderExists(targetFolder))
throw new DirectoryNotFoundException("Target folder doesn't exist " + targetFolder);
logger.Info("Verifying Update Folder");
if (!_diskProvider.FolderExists(_environmentProvider.GetUpdatePackageFolder()))
throw new DirectoryNotFoundException("Update folder doesn't exist " + _environmentProvider.GetUpdatePackageFolder());
}
public virtual void Start(string targetFolder)
{
Verify(targetFolder);
var appType = AppType.Normal;
logger.Info("Stopping all running services");
if (_serviceProvider.ServiceExist(ServiceProvider.NZBDRONE_SERVICE_NAME)
&& _serviceProvider.IsServiceRunning(ServiceProvider.NZBDRONE_SERVICE_NAME))
{
appType = AppType.Service;
try
{
_serviceProvider.Stop(ServiceProvider.NZBDRONE_SERVICE_NAME);
}
catch (InvalidOperationException e)
{
logger.WarnException("couldn't stop service", e);
}
}
else
{
appType = AppType.Normal;
}
//TODO:Should be able to restart service if anything beyond this point fails
logger.Info("Killing all running processes");
if (_processProvider.GetProcessByName(ProcessProvider.NzbDroneConsoleProcessName).Any())
{
appType = AppType.Console;
_processProvider.KillAll(ProcessProvider.NzbDroneConsoleProcessName);
}
_processProvider.KillAll(ProcessProvider.NzbDroneProcessName);
logger.Info("Creating backup of existing installation");
_diskProvider.CopyDirectory(targetFolder, _environmentProvider.GetUpdateBackUpFolder());
logger.Info("Moving update package to target");
try
{
_diskProvider.CopyDirectory(_environmentProvider.GetUpdatePackageFolder(), targetFolder);
}
catch (Exception e)
{
RollBack(targetFolder);
foreach (var key in e.Data.Keys)
{
logger.Trace("Key: {0}, Value: {1}", key, e.Data[key]);
}
logger.FatalException("Failed to copy upgrade package to target folder.", e);
}
finally
{
StartNzbDrone(appType, targetFolder);
}
}
private void RollBack(string targetFolder)
{
//TODO:this should ignore single file failures.
logger.Info("Attempting to rollback upgrade");
_diskProvider.CopyDirectory(_environmentProvider.GetUpdateBackUpFolder(), targetFolder);
}
private void StartNzbDrone(AppType appType, string targetFolder)
{
logger.Info("Starting NzbDrone");
if (appType == AppType.Service)
{
logger.Info("Starting NzbDrone service");
_serviceProvider.Start(ServiceProvider.NZBDRONE_SERVICE_NAME);
}
else if (appType == AppType.Console)
{
logger.Info("Starting NzbDrone with Console");
_processProvider.Start(Path.Combine(targetFolder, "NzbDrone.Console.exe"));
}
else
{
logger.Info("Starting NzbDrone without Console");
_processProvider.Start(Path.Combine(targetFolder, "NzbDrone.exe"));
}
}
}
}

View File

@ -0,0 +1,38 @@
using NLog;
using NzbDrone.Common;
namespace NzbDrone.Update.UpdateEngine
{
public interface IBackupAndRestore
{
void BackUp(string source);
void Restore(string target);
}
public class BackupAndRestore : IBackupAndRestore
{
private readonly IDiskProvider _diskProvider;
private readonly IEnvironmentProvider _environmentProvider;
private readonly Logger _logger;
public BackupAndRestore(IDiskProvider diskProvider, IEnvironmentProvider environmentProvider, Logger logger)
{
_diskProvider = diskProvider;
_environmentProvider = environmentProvider;
_logger = logger;
}
public void BackUp(string source)
{
_logger.Info("Creating backup of existing installation");
_diskProvider.CopyDirectory(source, _environmentProvider.GetUpdateBackUpFolder());
}
public void Restore(string target)
{
//TODO:this should ignore single file failures.
_logger.Info("Attempting to rollback upgrade");
_diskProvider.CopyDirectory(_environmentProvider.GetUpdateBackUpFolder(), target);
}
}
}

View File

@ -0,0 +1,38 @@
using System.Linq;
using NzbDrone.Common;
namespace NzbDrone.Update.UpdateEngine
{
public interface IDetectApplicationType
{
AppType GetAppType();
}
public class DetectApplicationType : IDetectApplicationType
{
private readonly IServiceProvider _serviceProvider;
private readonly IProcessProvider _processProvider;
public DetectApplicationType(IServiceProvider serviceProvider, IProcessProvider processProvider)
{
_serviceProvider = serviceProvider;
_processProvider = processProvider;
}
public AppType GetAppType()
{
if (_serviceProvider.ServiceExist(ServiceProvider.NZBDRONE_SERVICE_NAME)
&& _serviceProvider.IsServiceRunning(ServiceProvider.NZBDRONE_SERVICE_NAME))
{
return AppType.Service;
}
if (_processProvider.GetProcessByName(ProcessProvider.NzbDroneConsoleProcessName).Any())
{
return AppType.Console;
}
return AppType.Normal;
}
}
}

View File

@ -0,0 +1,82 @@
using System;
using System.IO;
using NLog;
using NzbDrone.Common;
namespace NzbDrone.Update.UpdateEngine
{
public interface IInstallUpdateService
{
void Start(string installationFolder);
}
public class InstallUpdateService : IInstallUpdateService
{
private readonly IDiskProvider _diskProvider;
private readonly IDetectApplicationType _detectApplicationType;
private readonly ITerminateNzbDrone _terminateNzbDrone;
private readonly IEnvironmentProvider _environmentProvider;
private readonly IBackupAndRestore _backupAndRestore;
private readonly IStartNzbDrone _startNzbDrone;
private readonly Logger _logger;
public InstallUpdateService(IDiskProvider diskProvider, IDetectApplicationType detectApplicationType, ITerminateNzbDrone terminateNzbDrone,
IProcessProvider processProvider, IEnvironmentProvider environmentProvider, IBackupAndRestore backupAndRestore, IStartNzbDrone startNzbDrone, Logger logger)
{
_diskProvider = diskProvider;
_detectApplicationType = detectApplicationType;
_terminateNzbDrone = terminateNzbDrone;
_environmentProvider = environmentProvider;
_backupAndRestore = backupAndRestore;
_startNzbDrone = startNzbDrone;
_logger = logger;
}
private void Verify(string targetFolder)
{
_logger.Info("Verifying requirements before update...");
if (String.IsNullOrWhiteSpace(targetFolder))
throw new ArgumentException("Target folder can not be null or empty");
if (!_diskProvider.FolderExists(targetFolder))
throw new DirectoryNotFoundException("Target folder doesn't exist " + targetFolder);
_logger.Info("Verifying Update Folder");
if (!_diskProvider.FolderExists(_environmentProvider.GetUpdatePackageFolder()))
throw new DirectoryNotFoundException("Update folder doesn't exist " + _environmentProvider.GetUpdatePackageFolder());
}
public void Start(string installationFolder)
{
Verify(installationFolder);
var appType = _detectApplicationType.GetAppType();
try
{
_terminateNzbDrone.Terminate();
_backupAndRestore.BackUp(installationFolder);
_logger.Info("Moving update package to target");
try
{
_diskProvider.CopyDirectory(_environmentProvider.GetUpdatePackageFolder(), installationFolder);
}
catch (Exception e)
{
_backupAndRestore.Restore(installationFolder);
_logger.FatalException("Failed to copy upgrade package to target folder.", e);
}
}
finally
{
_startNzbDrone.Start(appType, installationFolder);
}
}
}
}

View File

@ -0,0 +1,45 @@
using System.IO;
using NLog;
using NzbDrone.Common;
namespace NzbDrone.Update.UpdateEngine
{
public interface IStartNzbDrone
{
void Start(AppType appType, string installationFolder);
}
public class StartNzbDrone : IStartNzbDrone
{
private readonly IServiceProvider _serviceProvider;
private readonly IProcessProvider _processProvider;
private readonly Logger _logger;
public StartNzbDrone(IServiceProvider serviceProvider, IProcessProvider processProvider, Logger logger)
{
_serviceProvider = serviceProvider;
_processProvider = processProvider;
_logger = logger;
}
public void Start(AppType appType, string installationFolder)
{
_logger.Info("Starting NzbDrone");
if (appType == AppType.Service)
{
_logger.Info("Starting NzbDrone service");
_serviceProvider.Start(ServiceProvider.NZBDRONE_SERVICE_NAME);
}
else if (appType == AppType.Console)
{
_logger.Info("Starting NzbDrone with Console");
_processProvider.Start(Path.Combine(installationFolder, "NzbDrone.Console.exe"));
}
else
{
_logger.Info("Starting NzbDrone without Console");
_processProvider.Start(Path.Combine(installationFolder, "NzbDrone.exe"));
}
}
}
}

View File

@ -0,0 +1,57 @@
using System;
using System.Linq;
using NLog;
using NzbDrone.Common;
using IServiceProvider = NzbDrone.Common.IServiceProvider;
namespace NzbDrone.Update.UpdateEngine
{
public interface ITerminateNzbDrone
{
void Terminate();
}
public class TerminateNzbDrone : ITerminateNzbDrone
{
private readonly IServiceProvider _serviceProvider;
private readonly IProcessProvider _processProvider;
private readonly Logger _logger;
public TerminateNzbDrone(IServiceProvider serviceProvider, IProcessProvider processProvider, Logger logger)
{
_serviceProvider = serviceProvider;
_processProvider = processProvider;
_logger = logger;
}
public void Terminate()
{
_logger.Info("Stopping all running services");
if (_serviceProvider.ServiceExist(ServiceProvider.NZBDRONE_SERVICE_NAME)
&& _serviceProvider.IsServiceRunning(ServiceProvider.NZBDRONE_SERVICE_NAME))
{
try
{
_serviceProvider.Stop(ServiceProvider.NZBDRONE_SERVICE_NAME);
}
catch (InvalidOperationException e)
{
_logger.WarnException("couldn't stop service", e);
}
}
//TODO:Should be able to restart service if anything beyond this point fails
_logger.Info("Killing all running processes");
if (_processProvider.GetProcessByName(ProcessProvider.NzbDroneConsoleProcessName).Any())
{
_processProvider.KillAll(ProcessProvider.NzbDroneConsoleProcessName);
}
_processProvider.KillAll(ProcessProvider.NzbDroneProcessName);
}
}
}